在计算机最底层,数据都是以二进制(01010)的补码方式存储,而计算机中最小的存储单位是位(bit),用来表示0或者1。
计算机中最基本的存储单位是字节(Byte),1个字节对应8个位(Bit)。
而平常应用中常使用的基本存储单位包括KB,MB,GB,TB。它们之间都是以1024换算的,以下所示html
1TB=1024GB
1GB=1024MB
1MB=1024KB
1KB=1024B
1B=8bit
如今一般笔记本的内存一般是8G,16G,32G,64G等等,而运行在笔记本之上的操做系统广泛都是64位的,由于32位系统只能使用4G内存,下面是4G的内存换算linux
4G=2^2 * 2^10 * 2^10 * 2^10 =4*1024*1024*1024=2^32
由于4G只可以寻址到2^32,使用16进制表示就是0xFFFFFFFF,这里能够借助Visual Studio的调试功能查看内存的寻址,以下图所示
源程序(memory_storage_32bit.c)以下所示web
#include <stdio.h>
#include <stdlib.h>
/* 32位数系统寻址 @author tony ittimeline@163.com @version 2018/11/19 17:19:07 */
void main() {
int age = 29;
printf("整数变量age的地址是%p\n",&age);
system("pause");
}
使用迅雷下载某些资源时的网速就是KB或者MB,而网络运营提供商(例如长城宽带、移)声称的百兆带宽其实是100Mb(bit),可是网络下载速度是以字节(KB)为单位的,所以真实的网速(下载速度)理论上只有100Mb/8=12.5MB面试
在购买内存或者买移动硬盘时,一般使用的存储单位就是GB或者是TB,
可是在买4T的移动硬盘时,实际的可用容量却只有3T多,由于计算机的存储单位是以2的10次方(即1024)换算,而硬盘厂商们是以1000为换算单位。算法
4T的硬盘换算成位以下所示express
4T=4*1024GB*1024MB*1024KB*1024B*8bit
而硬盘厂商的实际容量编程
4T=1000*1000*1000*1000*8
所以实际的可用容量是ubuntu
4*1000*1000*1000*1000/1024/1024/1024/1024≈3.63T
而在一些互联网巨头(例如国内的BAT,国外的亚马逊、苹果、微软、谷歌,脸书)公司中,可能使用到比TB更大的海量数据,也就是PB或者EB,它们的换算单位以下所示。windows
1PB=1024TB
1EB=1024PB
内存在程序看来就是有地址编号的一块连续空间,当数据放到内存中后,为了方便的找到和操做这个数据,须要给这个位置起名字,编程语言经过变量来表示这个过程。数组
在使用变量前必须先要声明变量并初始化赋值,而且要遵照变量的命名规范
以下应用案例(Chapter2/variable/variable_declare.c)所示展现了C语言的变量命名案例
#include <stdio.h>
#include <stdlib.h>
/* 变量的声明赋值及其命名规范 @author tony ittimeline@163.com @version 2018/11/19 17:21:07 */
void main() {
//合法的标识符
int number;
//见名知意
int age;
char ch;
double db;
//变量名不能是关键字
//int void;
//变量名不能以数字开头
//int 1num;
/****************************************编译器特性*******************************/
//VC支持中文变量,GCC不支持中文命名
int 年龄 = 29;
printf("年龄 =%d\n", 年龄);
//在老版(C++11以前)的编译器中,变量声明必须放在函数调用以前
/****************************************编译器特性*******************************/
//声明多个变量
int one, two, three;
system("pause");
}
在声明变量后,必定要给变量赋初始值,否者没法编译经过,以下应用案例(Chapter2/variable/variable_not_init.c)所示
#include <stdio.h>
#include <stdlib.h>
/* 变量初始化赋值 在使用变量时必须手动初始化赋值,不然会获得一个随机的垃圾值 @author tony ittimeline@163.com @version 2018/11/24 17:58:07 */
void main() {
int num;
//编译错误错误 C4700 使用了未初始化的局部变量“num”
printf("num =%d\n", num);
system("pause");
}
以下应用程序(Chapter2/variable/variable_storage.c)所示,经过"="能够给变量赋值,同时能够经过printf()函数传递%p参数来获取变量在内存中的地址。
#include <stdio.h>
#include <stdlib.h>
/* 变量在内存中的存储 @author tony ittimeline@163.com @version 2018/11/24 17:58:07 */
void main() {
int num = 20;
//查看num的内存地址
printf("整数变量num的地址是%p\n", &num);
printf("整数变量num = %d\n", num);
num = 30;
printf("修改以后整数变量num的值是%d\n", num);
system("pause");
}
以下图所示,还能够经过Visual Studio 提供的调试功能经过断点查看变量在内存的存储,经过输入变量的内存地址即可以观察变量对应的值。
在同一时刻,内存地址对应的值只能存储一份,若是修改地址对应的值,以前的值会被覆盖,这个就是变量的特色,变量名是固定的,可是变量值在内存中是随着业务逻辑在变化的,例如最多见的游戏场景中,游戏人物生命值的变化。
当在程序中声明变量并赋值时,编译器会建立变量表维护变量的信息,包括变量的地址,变量的类型以及变量的名称。
而在内存中变量的内存地址和变量值是一一对应的,编译器正是经过变量表的内存地址和内存中的变量地址关联。所以在使用变量进行相关操做以前必须先声明并赋值,不然程序会发生编译错误,以下应用案例(Chapter2/variable/variable_compiler.c)所示。
#include <stdio.h>
#include <stdlib.h>
/* 编译器和内存对变量的处理 @author tony ittimeline@163.com @version 2018/11/24 17:27:07 */
void main() {
int a, b, c;
//这里会发生编译错误 不能使用未声明的变量
//printf(" %d\n", d);
system("pause");
}
当两个变量在执行相关运算(例如加法)时,系统会将把两个变量地址对应的变量值移动到CPU内部的寄存器中执行运算后将运算结果返回给内存,以下应用案例(Chapter2/variable/variable_operator_principle.c)所示
#include <stdio.h>
#include <stdlib.h>
/* 变量运算的原理 @author tony ittimeline@163.com @version 2018/11/24 18:26:07 */
void main() {
int a = 1;
int b = 2;
//分配四个字节的内存
int c;
printf("变量a的地址是%p\t,变量b的地址是%p\t,变量c的地址是%p\n", &a, &b, &c);
//数据的运算是在CPU的寄存器完成的
c = a + b;
c = b - a;
//对数据的操做是由CPU完成的
//a + 1 = 4;
printf("c=%d\n", c);
system("pause");
}
以下图所示,能够借助VisualStudio的调试功能来观察EAX寄存器的变化的值。
为了可以更加直接的理解寄存器的做用,这里使用C语言嵌套汇编语言来经过指令操做寄存器完成变量的赋值运算和加法运算,应用案例(Chapter2/variable/variable_asm_assignment)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* 使用汇编语言实现变量的赋值以及运算来理解数据的运算是在CPU内部的寄存器完成的 @author tony ittimeline@163.com @version 2018/11/24 18:30:07 */
void main() {
//申请四个字节的内存
int a;
printf("整数变量a的地址是%p\n", &a);
//变量的赋值都是经过CPU的寄存器来完成的
//这里借助汇编语言实现将10赋值给变量a
_asm {
mov eax, 10
mov a, eax
}
printf("整数变量a的值等于%d\n", a);
_asm {
//把变量a的值赋值给寄存器eax
mov eax, a
//将eax的值加5
add eax, 5
//把eax的值赋值给a
mov a, eax
}
printf("变量a加5以后的结果是%d\n", a);
system("pause");
}
变量的交换,能够经过采用中间变量,算术(加减法或者乘除法)运算、异或运算
三种方式实现,其应用场景主要在使用在排序算法中,每种实现变量交换方法的时空复杂度有不一样的考量。
#include <stdio.h>
#include <stdlib.h>
/* 使用临时变量实现变量交换 赋值运算三次 增长空间 @author tony ittimeline@163.com @version 2018/11/24 18:32:07 */
void varriable_swap_with_tmp(int left, int right) {
printf("使用临时变量实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
int middle = left;
left = right;
right = middle;
printf("使用临时变量实现变量交换交换以后\t left=%d \t right=%d\n", left, right);
}
/* 变量交换 @author tony ittimeline@163.com @version 2018/11/24 18:32:07 */
void main() {
int left = 5;
int right = 10;
varriable_swap_with_tmp(left, right);
system("pause");
}
#include <stdio.h>
#include <stdlib.h>
/* 使用算术运算实现变量交换 考虑数据越界的问题 不须要开辟额外的空间 赋值运算三次,算术运算三次 总运算次数6次 @author tony ittimeline@163.com @version 2018/11/24 18:38:07 */
void variable_swap_with_algorithm(int left, int right) {
printf("使用算术运算实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
left = left + right; // 加号变成乘号
right = left - right;//减号变成除号
left = left - right; //减号变成除号
printf("使用算术运算实现变量交换交换以后\t left=%d \t right=%d\n", left, right);
}
/* 使用算术运算实现变量交换 @author tony ittimeline@163.com @version 2018/11/24 18:39:07 */
void main() {
int left = 5;
int right = 10;
variable_swap_with_algorithm(left, right);
system("pause");
}
#include <stdio.h>
#include <stdlib.h>
/* 使用异或运算实现变量交换 不用考虑运算结果溢出的问题 @author tony ittimeline@163.com @version 2018/11/24 18:40:07 */
void variable_swap_with_xor(int left, int right) {
printf("使用异或运算实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
left = left ^ right;
right = left ^ right;
left = left ^ right;
printf("使用异或运算实现变量交换交换以后\t left=%d \t right=%d\n", left, right);
}
/* 使用异或实现变量交换 @author tony ittimeline@163.com @version 2018/11/24 18:41:07 */
void main() {
int left = 5;
int right = 10;
variable_swap_with_xor(left, right);
system("pause");
}
在函数中的形式参数和代码块中的局部变量都是自动变量,它们的特色是只有在定义的时候才会被建立(即系统自动开辟内存空间),在定义它们的函数返回时系统自动回收变量占据的内存空间,为了考虑到代码的可读性,一般使用auto关键字来修饰自动变量,应用案例(Chapter2/variable/auto_variable.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 自动变量: 只有定义它们的时候才建立,在定义它们的函数返回时系统回收变量所占用的存储空间, 对这些变量存储空间的分配和回收由系统自动完成 通常状况下,不作专门说明的变量都是自动变量,自动变量也可使用关键字auto说明 块语句中的变量,函数的形式参数都是自动变量 @author tony ittimeline@163.com @version 2018/11/24 18:42:07 */
void auto_varriable(auto int num) { //num就是自动变量,函数调用的时候就存在,函数结束,变量会被操做系统自动回收,地址都是同一个地址,可是值在不断发生变化
printf("num的内存地址是%p\nnum的值是%d\n", &num, num);
auto int data = num;
printf("data的内存地址是%p\ndata的值是%d\n", &data, data);
}
/* 屡次调用自动变量 @author tony ittimeline@163.com @version 2018/11/24 18:42:07 */
void invoke_auto_varriable() {
int num = 20;
auto_varriable(num);
printf("\n\n");
auto_varriable(80);
}
/* 自动变量测试入口 @author tony ittimeline@163.com @version 2018/11/24 18:42:07 */
void main() {
invoke_auto_varriable();
system("pause");
}
能够经过下断点来调试该程序,观察当执行auto_varriable()函数完成之后,局部变量data将会被回收,以下图所示
同时能够经过观察内存地址,发现当调用auto_varriable()函数时,num=20
而后当执行完auto_varriable()函数后,num的值变量一个系统分配的垃圾值
而静态变量不会发生变化,即便函数执行完成也不会被操做系统回收,应用案例(Chapter2/variable/static_variable.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 静态变量 @author tony ittimeline@163.com @version 2018/11/24 18:43:07 */
void static_varriable() {
static int x = 99;
printf("x的内存地址是%p,x的值是%d", &x, x);
printf("\n\n");
}
/* 屡次调用静态变量 @author tony ittimeline@163.com @version 2018/11/24 18:43:07 */
void invoke_static_varriable() {
static_varriable();
printf("\n");
static_varriable();
}
/* 静态变量测试入口 @author tony ittimeline@163.com @version 2018/11/24 18:43:07 */
void main() {
//invoke_auto_varriable();
invoke_static_varriable();
system("pause");
}
调试以上应用程序,会发现直到main函数执行完成,静态整数变量x都不会被操做系统回收。
常量表示一旦初始化以后便不能再次直接改变的变量,例如人的身份证编号一旦肯定以后就不会再次改变。C语言支持使用const关键字和#define CONST_NAME CONST_VALUE 两种方式来定义和使用常量。
若是想要使一个变量变成常量,只须要在变量前面使用const关键字便可,const常量虽然不能直接修改,可是能够经过C语言的指针来修改,所以不是真正意义上的常量。,应用案例(Chapter2/const/const.c)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* const常量不能直接修改值,可是能够经过指针修改值 @author tony ittimeline@163.com @version 2018/11/24 18:56:07 */
void main() {
//定义一个整数常量
const long id = 10000;
//不能直接修改常量
//id = 10001;
printf("常量id的地址是%p\n", &id);
printf("常量id=%d\n", id);
//经过指针修改
//* 根据地址取内容
//(int*) 类型转换为非 常量类型
* (int*)(&id) = 10001;
printf("常量id=%d\n", id);
system("pause");
}
在C语言中使用const定义的变量不能直接修改,可是能够经过指针来修改,所以不是真正意义上的常量。
若是想要使用真正意义上的常量,可使用#define CONSTA_NAME VALUE 来实现,应用案例(Chapter2/const/define.c)以下所示
#include <stdio.h>
#include <stdlib.h>
//#define语句不须要分号结尾,#define定义的常量值是在寄存器中产生,没法取内存地址,即没法经过C语言修改,
//由于C语言没法直接操做CPU的寄存器,只能操做内存。
#define CARD_NUMBER 88888888
/* define常量 @author tony ittimeline@163.com @version 2018/11/24 19:15:07 */
void main() {
printf("CARD_NUMBER=%d\n", CARD_NUMBER);
system("pause");
}
使用#define定义常量的好处:
应用案例以下所示(Chapter2/const/define_with_method_reference.c)
#include <stdio.h>
#include <stdlib.h>
#define CARD_NUMBER 88888888
/* 在自定义方法中使用常量 @author tony ittimeline@163.com @version 2018/11/24 19:18:07 */
void use_card_number_const() {
printf("在自定义方法中使用CARD_NUMBER常量的值=%d\n", CARD_NUMBER);
}
/* define常量的好处 定义后能够在任意的代码块引用 @author tony ittimeline@163.com @version 2018/11/24 19:18:07 */
void main() {
use_card_number_const();
system("pause");
}
使用define定义常量实现代码混淆,应用案例(Chapter2/const/define_app.c)以下所示
首先在define_app.h头文件中定义以下常量
#define _ void
#define __ main()
#define ___ {
#define ____ system("notepad");
#define _____ system("pause");
#define ______ }
而后定义define_app.c源文件,内容以下
#include "define.h"
/* 使用define实现代码混淆 @author tony ittimeline@163.com @version 2018/11/24 19:18:07 */
_ __ ___ ____ _____ ______
运行程序后,能够打开记事本。
在计算机内存中,都是以二进制(01001)的补码形式来存储数据的,而在生活中以十进制方式计算的数据居多,例如帐户余额,薪水等等。而计算的内存地址一般都是使用十六进制展现的,Linux系统的权限系统采用八进制的数据运算。相同进制类型数据进行运算时会遵照加法:逢R进1;减法:借1当R,其中R就表示进制的值,例如2,8,10,16。
以下表格是它们的组成、示例和使用场景:
进制名称 | 组成 | 数值示例 | 典型使用场景 |
---|---|---|---|
二进制 | 0,1 | 0101 | 内存数据存储 |
八进制 | 0-7之间的8个整数 | 012(以0开头) | linux权限 |
十进制 | 0-9之间的10个整数 | 12 | 整数 |
十六进制 | 0-9,a-f之间的10个数字加6个字母 | 12f | 数据的内存地址 |
以下应用案例(Chapter2/datatype/datatype_radix_int.c)就是八进制、十六进制和十进制的变量使用,须要注意的是C语言中的整数默认就采用十进制来表示,并且c语言没有提供二进制的数据表现形式。
#include <stdio.h>
/* 整数常见的几种进制类型 @author tony ittimeline@163.com @date 2017/10/31 20:27 @website www.ittimeline.net */
void main() {
//八进制:0开头 0-7之间的八个数字表示
int oct_val = 017; //15
//十六进制:0x开头0-9,a-f之间的十个数字加上6个字母
int hex_val = 0x12;//18
//C语言不支持声明2进制的变量,这里是十进制的值
int binary_val = 101001;
printf("oct_val = %d\t hex_val =%d,binary_val=%d\n",oct_val,hex_val,binary_val);
system("pause");
}
在某些场景下(例如面试)须要完成经常使用进制之间的数据转换
根据小学数学的逻辑能够知道23=8,24=16, 它们三者之间能够这样换算
二进制转八进制:在转换时,从右向左,每三位一组(不足三位用0补齐),转换成八进制。
二进制和八进制的对应关系以下表:
二进制 | 八进制 |
---|---|
000 | 0 |
001 | 1 |
010 | 2 |
011 | 3 |
100 | 4 |
101 | 5 |
110 | 6 |
111 | 7 |
例如 1010转换为八进制的结果为12
二进制和十六进制的对应关系以下表:
二进制 | 十六进制 |
---|---|
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
1010 | a |
1011 | b |
1100 | c |
1101 | d |
1110 | e |
1111 | f |
二进制转十六进制:从右向左,每4位一组(不足4位,用0补齐),转换成十六进制。
例如 1010转换为八进制的结果为a
二进制、八进制、十六进制转换成十进制都是采用按权相加的形式计算的。
先看一个整数计算的案例:
1234=1*10^3+2*10^2+3*10^1+4*10^0=1000+200+30+4
所以能够采用按权相加的计算方式将二进制、八进制、十六进制转换为十进制
二进制转换为十进制:
100101=1*2^5+1*2^2+1*2^0=32+4+1=37
八进制转换为十进制
27=2*8^1+7*8^0=16+7=23
十六进制转换为十进制
20d=2*16^2+13*16^0=512+13=525
十进制整数转换为二进制:方法是除以2取余,直到商数为0,逆序排列,以22为例:倒序的二进制结果就是10110
计算过程以下:
22/2 余 0
11/2 余 1
5 /2 余 1
2/2 余 0
1/2 余 1
0 商数为0
以下应用案例所示(Chapter2/datatype/datatype_radix_convert_app.c),采用编程实现十进制转二进制案例
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 0-32767之间的十进制转换成二进制:除以2取余,直到商数为0,逆序排列 @author tony ittimeline@163.com @version 2018/11/24 20:08:07 */
void main() {
int numbers[16] = { 0 }; //初始化一个16位整数数组,用于保存二进制整数
printf("请输入一个须要转换成二进制的十进制正整数,取值范围在0-32767之间\n");
int value = 0; //待转换的十进制整数
scanf("%d", &value); //读取用户输入的整数
for (int i = 0; i < 15; i++) { //十六位二进制数,最高符号位为正整数
int quotient = value / 2; //商数
int remainder = value % 2; //余数
value = quotient; //将除以2以后的商数赋值给value
numbers[i] = remainder;//将余数存储在数组中
}
printf("十进制%d对应的二进制整数为", value);
//逆序输出二进制
for (int i = 15; i >= 0; i--) {
printf("%d ", numbers[i]);
}
printf("\n");
system("pause");
return 0;
}
使用C语言的库函数_itoa实现进制的转换的应用案例(Chapter2/datatype/datatype_radix_convert_itoa.c):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 使用C语言的库函数_itoa实现经常使用的进制转换 @author tony ittimeline@163.com @version 2018/11/24 20:12:07 */
void main() {
printf("请输入须要转换二进制的十进制整数\n");
int num = 0;
scanf("%d", &num);
char str[64] = { 0 };
_itoa(num, str, 2);
printf("%d转换为二进制的结果是%s\n", num, str);
_itoa(num, str, 10);
printf("%d转换为十进制的结果是%s\n", num, str);
_itoa(num, str, 8);
printf("%d转换为八进制的结果是%s\n", num, str);
_itoa(num, str, 16);
printf("%d转换为十六进制的结果是%s\n", num, str);
system("pause");
}
原码 | 反码 | 补码 | |
---|---|---|---|
+7 | 00000111 | 00000111 | 00000111 |
-7 | 10000111 | 11111000 | 11111001 |
+0 | 00000000 | 00000000 | 00000000 |
-0 | 10000000 | 11111111 | 00000000 |
数的取值范围 | -127-127 | -127-127 | -128-127 |
从上面的表格能够看出,正数的原码、反码和补码都相同,而负数的补码就是原码取反(最高位不变,其余位取反)后加1的结果。
而实际数据在计算机(手机、电脑、服务器)的内存中也是以补码的形式存储数据的,以下应用案例(Chapter2/datatype/datatype_code.c)所示
#include <stdio.h>
#include <stdlib.h>
/* 原码、反码、补码的运算 @author tony ittimeline@163.com @version 2018/11/24 20:21:07 */
void main() {
//原码 10000111
//反码 11111000
//补码 1111 1001 F9
char ch = -7;
printf("ch的地址是%p\n", &ch);
printf("ch=%d\n", ch);
system("pause");
}
首先须要计算出-7的补码,而后转换为16进制的结果为F9,而后经过Visual Studio的调试功能查看内存的存储结果,以下图所示
数据类型即对数据进行分类,数据在计算机底层是二进制的,不太方便操做,所以编程语言引入了数据类型将其进行分类处理。
不一样的数据类型占据不一样的内存大小,这里可使用C语言提供的sizeof()运算符来获取指定数据类型占据的字节数量,应用案例(Chapter2/datatype/sizeof.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 使用sizeof()关键字获取指定数据类型的大小 @author tony ittimeline@163.com @version 2018/11/24 19:42:07 */
void main() {
printf("char占据的字节数量是%d\n", sizeof(char));
printf("short占据的字节数量是%d\n", sizeof(short));
printf("int占据的字节数量是%d\n", sizeof(int));
printf("double占据的字节数量是%d\n", sizeof(double));
system("pause");
}
``
固然sizeof()还能够求表达式的数据类型,应用案例(Chapter2/datatype/sizeof_expression.c)以下所示
``` c
#include <stdio.h>
#include <stdlib.h>
/* 使用sizeof求表达式的内存大小 @author tony ittimeline@163.com @version 2018/11/24 19:44:07 */
void main() {
int num = 10;
printf("字符串str占据的字节数量是%d\n", sizeof("str"));//4个字节 字符串以\0结束
char ch = 'A';
printf("字符变量ch占据的字节数量是%d\n", sizeof(ch));
printf("字符常量A占据的字节数量是%d\n", sizeof('A'));
printf("整数变量num占据的字节数量是%d\n", sizeof(num));
system("pause");
}
一样的数据,按照不一样的解析方式会获得不一样的结果,应用案例(Chapter2/datatype/datatype_analysis.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 数据的解析 @author tony ittimeline@163.com @version 2018/11/24 19:48:07 */
void main() {
//一样的数使用不一样的方式解析获取不一样的结果
int num = -1;
printf("num的内存地址是%p\n", &num);
getchar();
system("pause");
}
启动程序调试,经过查看控制台输出num变量的地址,而后在内存中分别以1字节带符号整数查看结果为-1,8字节整数查看结果为14757395259826634751,1字节不带符号显示(结果为255),以下图所示,不一样的方式查看经过鼠标右键获取。
而若是数据使用了错误的解析方式,则结果也会发生错误,这里以printf()函数为例子,应用案例(Chapter2/datatype/datatype_printf_analysis.c)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* printf解析数据类型 @author tony ittimeline@163.com @version 2018/11/24 19:53:07 */
void main() {
//printf函数不会进行类型转换,当类型不匹配输出就是错误的结果
int num = 10;
printf("num =%f\n", num);//若是想要获取正确的结果须要收到强制类型转换(float)num
//浮点数按照整数解析,结果会出错
float fl = 10.9;
printf("fl=%d\n", fl);//若是想要获取正确的结果须要收到强制类型转换(int)fl
system("pause");
}
每种数据类型都有本身的极限值(即最大值和最小值),若是在参与运算时超过了极限值,则运算结果是错误的,应用案例(Chapter2/datatype/datatype_unsigned_char_limits.c)以下所示
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/* 无符号的字符类型char的极限值越界 @author tony ittimeline@163.com @version 2018/11/24 19:54:07 */
void main() {
//为了保证结果运算正确,必须在极限范围以内
unsigned char chnum = UCHAR_MAX+1;
printf("无符号char所能存储的最大值是%d\n", UCHAR_MAX);
printf("chnum=%d", chnum); //结果为0 由于chnum所能表示的最大值为255,这里发生了越界,结果错误
system("pause");
}
整数的极限值定义在<limits.h>
头文件中,
浮点数的极限值定义在<float.h>
头文件中,
以下应用案例(Chapter2/datatype/datatype_int_float_limits.c)所示展现了整数以及浮点数的极限值使用。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>
/* 整数的极限 */
void int_limt() {
printf("int所能存储的最大值是%d,int所能存储的最小值是%d\n", INT_MAX, INT_MIN);
}
/* float的极限 */
void float_limit() {
printf("float所能存储的最大值是%e,float所能存储的最小值是%e\n", FLT_MAX, FLT_MIN);
printf("double所能存储的最大值是%e,float所能存储的最小值是%e\n", DBL_MAX, DBL_MIN);
}
/* 整数和浮点数的极限 @author tony ittimeline@163.com @version 2018/11/24 19:56:07 */
void main() {
int_limt();
float_limit();
system("pause");
}
在最底层,计算机的数据都是以二进制的形式表示的,那么如何区分正负数呢?
最高位(左边第一位)是符号位,若是是1,则表示为负数,若是是0则表示正数。
以下应用案例(Chapter2/datatype/datatype_signed_unsigned.c)所示
#include <stdio.h>
#include <stdlib.h>
/* 数据类型的正负 @author tony ittimeline@163.com @version 2018/11/24 19:48:07 */
void main() {
char ch = -1; //十六进制表示为ff 转换为二进制 1111 1111 最高位(左边第一个数字)为符号位,1表示负数
char chx = 3; //十六进制为03 转换为二进制为 0000 0011 最高为(左边第一个数字)为符号位,0表示整数
printf("字符变量ch的地址是%p,字符变量chx变量的地址是%p\n", &ch, &chx);
printf("ch=%d\tchx=%d\n", ch, chx);
system("pause");
}
PC、手机的内存排列是低位在低字节,高位在高字节,节省寻址时间。
以下应用程序所示
#include <stdio.h>
#include <stdlib.h>
/* 数据在内存中的排列 低位在低字节,高位在高字节 @author tony ittimeline@163.com @version 2018/11/24 20:02:07 */
void main() {
// 四字节二进制方式 0000 0000 0000 0000 0000 0000 0000 0001
int num = 1;
printf("num的地址是%p\n", &num);
printf("num = %d\n", num);
system("pause");
}
能够经过Visual Studio 下断点调试程序,使用1字节查看整数1在内存中的排列,以下图所示:
C语言整数常量可使用八进制,十进制和十六进制表示。
同时可使用u后缀表示位无符号整数,使用l后缀表示long类型的整数,使用ll后缀表示为long long类型的整数,应用案例(Chapter2/datatype/int/int_type_raidx.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 整数的三种进制类型 整数的三种后缀 无符号,长整数,长长整数 @author tony ittimeline@163.com @version 2018/11/24 20:23:07 */
void main() {
int a1 = 10;
int a2 = 010;
int a3 = 0x10;
int a4 = 101u; //无符号
int a5 = 102l;//long
int a6 = 103ll;//long long
printf("a1 = %d\ta2 = %d\ta3 = %d\ta4 = %d\ta5 = %d\ta6 = %d\t", a1, a2, a3, a4, a5, a6);
system("pause");
}
并且整数按照占据不一样的字节大小能够分为short,int,long和long long 四种类型,它们默认是有符号(signed)类型用于存储正负数,而对应的无符号类型则用来存储非负数的整数,关于它们可以存储数据的极限以及占据内存的大小以下应用案例(Chapter2/datatype/int/int_limits.c)所示。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/* C语言不一样类型整数的极限 占据不一样字节大小的整数极限也不同 @author tony ittimeline@163.com @version 2018/11/24 20:24:07 */
void main() {
//16位(嵌入式系统) int和short是等价的
printf("short能存储的最大值是%d\tshort能存储的最小值是%d,占据的字节数量是%d\n", SHRT_MAX, SHRT_MIN, sizeof(short));
printf("unsigned short能存储的最大值是%d\n", USHRT_MAX);
//32位和64位系统 int和long是等价的
printf("int能存储的最大值是%d\tint能存储的最小值是%d,占据的字节数量是%d\n", INT_MAX, INT_MIN, sizeof(int));
printf("unsigned int能存储的最大值是%d\n", UINT_MAX);
//无符号的整数 最小值都是0 即不能表示负数
printf("long能存储的最大值是%d\tlong能存储的最小值是%d,占据的字节数量是%d\n", LONG_MAX, LONG_MIN, sizeof(long));
printf("long long能存储的最大值是%lld\tlong long能存储的最小值是%lld,占据的字节数量是%d\n", LLONG_MAX, LLONG_MIN, sizeof(long long));
printf("unsigned long long 能存储的最大值是%llu\n", ULLONG_MAX);
system("pause");
}
在应用开发时须要主要使用数据类型的极限,若是超越数据存储范围的极限,程序会出现Bug,例如想要存储QQ或者手机号就应该使用无符号的long long 类型,应用案例(Chapter2/datatype/int/long_long.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* long long 的应用场景 @author tony ittimeline@163.com @version 2018/11/24 20:23:07 */
void main() {
unsigned long long mobilePhone = 18601767221;
printf("mobilePhone=%llu\n", mobilePhone);
unsigned long long qq = 1079351401;
printf(" qq = %llu", qq);
system("pause");
}
在使用整数参与运算时,须要考虑到数据范围对应的极限,不然会发生错误的结果,应用案例(Chapter2/datatype/int/short_int_over_flow.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 整数的越界 @author tony ittimeline@163.com @version 2018/11/24 20:26:07 */
void main() {
//为了保证结果运算正确,必须在极限范围以内
unsigned short int shortnum = USHRT_MAX+1;
printf("无符号short int所能存储的最大值是%d\n", USHRT_MAX);
printf("shortnum=%d", shortnum); //结果为0 由于unsigned short所能表示的最大值为65535,这里发生了越界,结果错误
system("pause");
}
C语言是在使用标准库的前提下是可移植的,可是C语言的整数在不一样的平台上,一样的数据类型占用的字节大小是不同的。例如int在16位系统占据2个字节,在32位及其以上系统占据四个字节,long在Windows平台上,不管是32位仍是64位都是占四个字节,而在64位ubuntu下却占据8个字节,应用案例以下所示
Linux版
#include <stdio.h>
/* @author tony ittimeline@163.com @version 2018/11/24 20:26:07 */
int main(){
long num=100;
int size=sizeof(num);
printf("ubuntu 64位系统中 long占据的字节数量是%d",size);
return 0;
}
Windows版
#include <stdio.h>
/* @author tony ittimeline@163.com @version 2018/11/24 20:26:07 */
void main() {
long val = 100;
printf("windows下long占据的字节数量是%d\n", sizeof(val));
getchar();
}
为了解决不一样平台,相同的类型占据的大小不一致的问题,C语言标准委员会在C99标准中提出了跨平台的整数,在<stdint.h>头文件中定义,意味着一样的类型在不一样的系统下的大小是一致的,应用案例以下所示
linux版
#include <stdio.h>
#include <stdint.h>
/* 跨平台的整数 @author tony ittimeline@163.com @version 2018/11/24 20:26:07 */
int main(){
long num=100;
int int_size=sizeof(num);
printf("ubuntu 64位系统中 long占据的字节数量是%d",int_size);
//在不一样的平台下占据都是32字节
int32_t int_32_MAX_VALUE = INT32_MAX;
int int32_size=sizeof(int32_t);
printf("sizeof(int_32_MAX_VALUE ) = %d\n",int32_size);
printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);
//在不一样的平台下占据都是64字节
int64_t int_64_MAX_VALUE = INT64_MAX;
int int64_size=sizeof(int64_t);
printf("sizeof(int_64_MAX_VALUE ) = %d\n", int64_size);
printf("int_64_MAX_VALUE = %ld\n", int_64_MAX_VALUE);
return 0;
}
windows版(Chapter2/datatype/int/int_std.c)
#include <stdio.h>
#include <stdint.h>
/* 不一样的平台,不一样的编译器,一样的数据类型大小不同。 例如int 16位的状况下是2个字节,32位系统是4个字节 long类型在windows上不管是32位仍是64位都是4个字节,而在64位linux上long占据的是8个字节 为了解决这个问题,C语言标准组织在C99标准中提出了跨平台的整数,也就是着不一样平台的整数占用的字节数量是同样的,VS2013+,GCC都支持该标准 */
/* 跨平台的整数 @author tony ittimeline@163.com @version 2018/11/24 20:26:07 */
void main() {
long val = 100;
printf("windows下long占据的字节数量是%d\n", sizeof(val));
//在不一样的平台下占据都是32字节
int32_t int_32_MAX_VALUE = INT32_MAX;
printf("sizeof(int_32_MAX_VALUE ) = %d\n", sizeof(int_32_MAX_VALUE));
printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);
//在不一样的平台下占据都是64字节
int64_t int_64_MAX_VALUE = INT64_MAX;
printf("sizeof(int_64_MAX_VALUE ) = %d\n", sizeof(int_64_MAX_VALUE));
printf("int_64_MAX_VALUE = %lld\n", int_64_MAX_VALUE);
getchar();
}
浮点数就是数学意义上的小数,C语言中分别使用float,double和long double表示,默认类型是double,浮点数的常量可使用十进制的小数和科学计数法表示,科学计数法能够存储特大或者特小的数字,应用案例(Chapter2/datatype/float/float_const.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 浮点数两种常量表示方法 @author tony ittimeline@163.com @version 2018/11/24 20:27:07 */
void main() {
//十进制
float flt = 12.0f; //小数后面加f表示float类型
double dbl = 12.0; //小数默认是double类型
//科学计数法
double db1 = 0.12e3;
//e以前必须有数字,指数必须为整数
double db2 = 12000.124e5; //e5表示10的5次方
//%f默认输出小数点后六位
printf("flt = %f \n", flt);
printf("db1 = %f \t db2 = %f\n", db1, db2);
getchar();
}
C语言在limits.h的头文件中使用常量定义了float和double的极限值,咱们能够尝试使用printf函数输出该结果,分别保留 800和1500位小数,应用案例(Chapter2/datatype/float/float_limits.c)以下所示。
#include <stdio.h>
#include <float.h>
/* 浮点数极限 @author tony ittimeline@163.com @version 2018/11/24 20:28:07 */
void main() {
//float占据四个字节,double占据8个字节long double 大于等于double
printf("float占据的字节数量是%d\tdouble占据的字节数量是%d long double占据的字节数量是%d\n\n\n\n\n", sizeof(float), sizeof(double), sizeof(long double));
printf("float能存储的最大值是%.100f\tfloat能存储的最小值是%.100", FLT_MAX, FLT_MIN);
printf("\n\n\n\n\n\n\n\n");
printf("double能存储的最大值是%.1500f\n\n\n\n double能存储的最小值是%.1500f\n", DBL_MAX, DBL_MIN);
getchar();
}
在进行赋值运算时会发生自动类型转换,例如把一个double类型的常量10.5赋值给float类型的变量,它们占据的字节数量不一样,可是可以赋值成功,由于发生了自动类型转换,应用案例(Chapter2/datatype/float/float_assignment_auto_convert.c)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* 赋值运算会发生自动类型转换 @author tony ittimeline@163.com @version 2018/11/24 20:29:07 */
void main() {
float flt = 10.5;
//程序输出结果显示flt和10.5占据的字节数量不一样,由于这里发生了数据类型转换
printf("flt占据的字节数量为%d\t 10.5占据的字节数量为%d", sizeof(flt), sizeof(10.5));
int num = 5 / 3;
printf(" num = %d\n", num);
int val = 3.2;
printf(" val =%d", val);
getchar();
}
float占据四个字节,提供的有效位是6-7位,而double占据八个字节,提供的有效位数是15-16位,若是在使用float或者double表示实数时超过有效数字,若拿来进行关系运算(例如等于)的话,会获得一个错误的结果,应用案例(Chapter2/datatype/float/float_equals.c)以下所示
/* 浮点数的相等性判断 若是实数超过有效范围,使用==判断会出错 @author tony ittimeline@163.com @version 2018/11/24 20:30:07 */
void main() {
float flt1 = 1.00000000001;
float flt2 = 1.00000000000000000001;
//由于float的有效数字是6-7位 这里超出有效数字 计算不许确
printf(" flt1 == flt2 ? %d\n", (flt1 == flt2)); // 输出结果1表示相等 0则表示不相等
//double精确的有效位数是15-16位,这里也超出了有效数字,计算不够正确
double db1 = 1.00000000000000000000000000000001;
double db2 = 1.000000000000000000000000000000000000000000000000000000000000000000000000000000001;
printf(" db1 == db2 ? %d\n", (db1 == db2)); // 输出结果1表示相等 0则表示不相等
system("pause");
}
int和float一样占据四个字节的内存,可是float所能表示的最大值比int大得多,其根本缘由是浮点数在内存中是以指数的方式存储。
咱们都知道在内存中,一个float类型的实数变量是占据32位,即32个二进制的0或者1组成
高位 低位
0000 0000 0000 0000 0000 0000 0000 0000
如上代码片断所示,从低位依次到高位叫第0位和第31位,这32位能够由三部分组成:
应用案例(Chapter2/datatype/float/float_storage.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 浮点数在内存中的存储 @author tony ittimeline@163.com @version 2018/11/24 20:32:07 */
void main() {
//符号位(31位) 阶码(第30位-23位) 尾数(第22位-第0位)
float flt1 = 10.0; //4字节十六进制 41200000 二进制 0 100 00001 010 0000 0000 0000 0000 0000
float flt2 = -10.0;//4字节十六进制 c1200000 二进制 1 100 00010 010 0000 0000 0000 0000 0000
printf(" flt1的内存地址是%p\tflt2的内存地址是%p\n", &flt1, &flt2);
float flt3 = 20.0; // 字节十六进制 41a00000 二进制 0 100 0001 1010 0000 0000 0000 0000 0000
printf("变量flt3的地址是%p", &flt3);
getchar();
}
使用math.h头文件中的sqrt函数实现给定三角形三边的面积计算,应用案例
(Chapter2/datatype/float/float_triangle.c)以下所示
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 根据给出的边长求面积 使用math.h文件中提供的开平方根函数 @author tony ittimeline@163.com @version 2018/11/24 20:32:07 */
void main() {
int a = 6;
int b = 8;
int c = 10;
int p = (a + b + c) / 2;
//sqrt返回float,这里使用赋值运算完成了类型转换
int s = sqrt(p * (p - a)*(p - b)*(p - c));
printf("s = %d", s);
printf("三角形的面积是%d", s);
printf("三角形的面积是%f", sqrt(p * (p - a)*(p - b)*(p - c)));
getchar();
}
使用math.h的pow函数实现中美GDP计算,并计算出中国GDP超过美国GDP的年份,应用案例
(Chapter2/datatype/float/float_gdp.c)以下所示
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 使用math.h的pow函数实现中美GDP计算,并计算出中国GDP超过美国GDP的年份 @author tony ittimeline@163.com @version 2018/11/24 20:29:07 */
void main() {
double ch_current_gdp = 12.0;
double us_current_gdp = 19.70;
double ch_rate = 1.06;
double us_rate = 1.04;
double ch_gdp;
double us_gdp;
int year;
for (int i = 1; i <= 100; i++) {
ch_gdp = ch_current_gdp * pow(ch_rate, i);
us_gdp = us_current_gdp * pow(us_rate, i);
year = 2017 + i;
printf("%d年中国的GDP是%f\n", year, ch_gdp);
printf("%d年美国的GDP是%f\n", year, us_gdp);
if (ch_gdp > us_gdp) {
printf("在%d年,中国的GDP超越了美国的GDP", year);
break;
}
}
getchar();
}
字符和字符串是平常开发中常常打交道的数据类型,使用一对单引号('')包含起来的内容就是字符,C语言提供了putchar()和printf()函数输出字符(英文),应用案例(Chapter2/datatype/char/char_io.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 输出字符的两种方式 putchar() printf("%c",char); @author tony ittimeline@163.com @version 2018/11/24 20:32:07 */
void main() {
putchar('A');
//输出中文乱码。
putchar('刘');
printf("%c", 'A');
system("pause");
}
在VisualStudio中运行基于C语言的控制台程序时,若是想要控制台等待(即程序中断),咱们可使用getchar()或者是system("pause")函数实现。
getchar()函数没有提示,只要一旦检测到单个字符,会当即吸取单个字符并返回,并且马上结束等待模式,退出程序。
而system("pause")函数具备提示,提示的内容为"请按任意键退出",应用案例以下所示
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* getchar()和system("pause")的区别 @author tony ittimeline@163.com @version 2018/12/17 23:57:07 */
void main() {
printf("请输入一个整数\n");
int num = 0;
scanf("%d",&num);
/* 若是使用getchar()函数,会发如今运行程序时,根据提示输入一个整数回车后程序将退出 getchar(); //若是想要解决该问题,还须要使用一个getchar()函数 getchar(); */
system("pause");
}
在平常开发中还会使用到一些经常使用的转义字符,例如\t
,\n
,\\
等等,它们表示的做用以下表格所示
转义字符 | 做用 |
---|---|
\t |
tab |
\n |
换行 |
\\ |
路径转义 |
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>
/* 经常使用的转义字符 @author tony ittimeline@163.com @version 2018/12/17 23:57:07 */
void main() {
//发出声音的两种方式
printf("%c",'\a');
Sleep(1000);
putchar('\a');
// \n实现文本内容换行
printf("学好C/C++,\n走遍天下都不怕");
printf("\n");
//路径转义 打开QQ
/* 函数参数说明 第一个参数:默认为0 第二个参数 操做->Open 第三个参数 命令->这里传递的是QQ的路径,并使用了转义字符 第四五个参数 默认0,0 第六个参数 若是是0表示隐式打开,1表示默认打开,3表示最大化打开,6表示最小化打开 */
ShellExecuteA(0, "Open", "\"C:/Program Files (x86)/Tencent/QQ/Bin/QQScLauncher.exe\"", 0, 0, 1);
system("pause");
}
字符还可使用十六进制或者是八进制的整数表示,以下应用案例所示
#include <stdio.h>
#include <stdlib.h>
/* 进制字符 @author tony ittimeline@163.com @version 2019/01/03 10:56 */
void main() {
//十六进制字符
printf("%c\n",'\x41'); //4*16+1=65 根据ASC||码计算的结果是字符A
//八进制字符
printf("%c\n",'/060'); //6*8=48 根据ASC||码计算的结果是字符0
//十六进制字符
printf("%c\n",'\x61'); // 6*16+1=97 根据ASC||码计算的结果是字符a
system("pause");
}
而字符常量一般为了考虑兼容和扩展宽字符(即中文),一般会占据4个字节,英文占据一个字节,中文占据两个字节,应用案例(Chapter2/datatype/char/char_sizeof.c)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* 字符的大小 字符常量为了兼容扩展宽字符,占据的字节数量都是4个字节 而英文字符占据一个字节,中文字符(宽字符)占据两个字节 @author tony ittimeline@163.com @version 2018/11/24 20:32:07 */
void main() {
char chinese = '刘';
//char 占据一个字节,没办法存储中文
printf("chinese =%c ", chinese);
char ch = 'A';
// sizeof()运算符求字符A的大小,这里为了兼容扩展宽字符,通常占据四个字节
printf(" ch占据的字节数量为%d\t 'A'占据的字节数量为%d\n", sizeof(ch), sizeof('A'));
//宽字符 占据两个字节,能够存储中文
wchar_t wch = L'我'; printf("宽字符占据的字节数量是%d\n", sizeof(wchar_t)); printf("字符常量我占据的字节数量是%d\n", sizeof('我')); }
若是要想输出中文字符,应用案例(Chapter2/datatype/wchar/chinese_char.c)以下所示
//引入本地化的头文件
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
/* 宽字符用于存储中文,可是如何输出中文呢? @author tony ittimeline@163.com @version 2018/11/24 20:33:07 */
void main() {
//设置本地化
setlocale(LC_ALL, "chs");
//宽字符 占据两个字节,能够存储中文
wchar_t wch = L'我'; //使用wprintf()函数输出中文 wprintf(L"%c\n", wch); getchar(); }
字符在内存中是以数字的方式存储,而ASC||码表规定了字符对应的数字编号,当使用printf()函数以数字的输出方式打印字符时,便输出了字符对应的ASC||码表的数字编号,应用案例以下所示
字符1和整数1的区别(Chapter2/datatype/char/char_num_1.c):
/* 字符型数据存储 字符在内存中是以数字存储的,ASC||表规定了字符对应的数字编号 @author tony ittimeline@163.com @version 2018/11/24 20:34:07 */
void main() {
char ch = '1';
int num = 1;
//字符1和数字1的区别:占据的字节数量不同
printf("字符1占据的字节数量是%d\t数字1占据的字节数量是%d\n", sizeof(ch), sizeof(num));
//字符1对应的数字是49,即求ASC码值
printf("字符1对应的ASC||码表的编号是%d\n", ch);
printf("ch=%c\n", ch);
printf("整数1对应的ASC||码表的字符是%c", num);
system("pause");
}
字符0,'\0'和整数0的区别(Chapter2/datatype/char/char_num_0.c)
#include <stdio.h>
#include <stdlib.h>
/* 字符0 对应的整数是48 整数0 对应的字符是空字符 \0对应的也是空字符,和整数0的效果同样 */
void main() {
char ch_zero = '0';
char ch_num_zero = 0;
int num_zero = 0;
char ch = '\0';
printf("ch_zero占据的字节数量是%d\tnum_zero占据的字节数量是%d\tch占据的字节数量是%d\n", sizeof(ch_zero), sizeof(num_zero), sizeof(ch));
printf("字符0对应的整数编号是%d\n", ch_zero);//48
printf("整数0对应的字符是[%c]\n", num_zero);
printf("\\0对应的整数编号是[%d]\n", ch);//0
printf("\\0的意义是[%c]\n", ch);//空字符
getchar();
}
字符应用(Chapter2/datatype/char/char_convert.c):实现大写转小写
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 经过ASC||码的规律实现大小写转换 @author tony ittimeline@163.com @version 2018/11/24 20:58:07 */
void main() {
char input = '\0';
printf("请输入一个字母\n");
scanf("%c", &input);
if (input >= 'A'&& input <= 'Z') {
printf("你输入的是大写字母,转换为小写字母的结果是%c\n", (input + 32));
}
else if (input >= 'a'&&input <= 'z') {
printf("你输入的是小写写字母,转换为小写字母的结果是%c\n", (input - 32));
}
system("pause");
}
字符串用于表示字符序列,也就是一串使用""
包含起来的内容,接下来使用system函数调用系统命令理解下什么是字符串,应用案例(Chapter2/dateype/string/string_system_invoke.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 字符串的应用场景 @author tony ittimeline@163.com @version 2018/11/24 21:00:07 */
void main() {
//改变窗口的颜色
system("color 4f");
//改变窗口的标题
system("title power by tony");
getchar();
}
C语言中的字符串以/0
结尾,这也就是意味着即便双引号""
中什么都没有也会占据一个字节,而中文字符串中的每一个字符一样会占据两个字节,应用案例(Chapter2/datatype/string/string_const.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 字符串常量 @author tony ittimeline@163.com @version 2018/11/24 21:14:07 */
void main() {
//字符串是以/0结尾,这里字符串A占据2个字节
printf("字符串A占据的字节数量是%d\n", sizeof("A"));
printf("\"\"占据的字节数量为%d\n", sizeof("")); //以\0结尾
//字符串单个中文占据两个字节
printf("字符串刘光磊占据的字节数量是%d", sizeof("刘光磊")); //每一个中文占据两个字节,而后以\0结尾 所以是7个
system("pause");
}
字符串加密解密应用案例(Chapter2/datatype/string/string_encryption.c)的实现
#include <stdio.h>
#include <stdlib.h>
/* 字符串简单的加密 @author tony ittimeline@163.com @version 2018/11/24 21:15:07 */
void main() {
char str[5] = { 'c','a','l','c','\0' };
system(str);
printf("加密以前str = %s\n", str);
for (int i = 0; i < 4; i++) {
str[i] += 1;
}
printf("加密以后str = %s\n", str);
for (int i = 0; i < 4; i++) {
str[i] -= 1;
}
printf("解密以后str = %s\n", str);
system("pause");
}
应用案例(Chapter2/datatype/string/string_sprintf_color.c):sprintf函数打印到字符串,而后借助color命令实现窗口变色
#include <stdio.h>
#include <stdlib.h>
/* 经过sprintf函数打印到字符串,而后借助color命令实现窗口变色 @author tony ittimeline@163.com @version 2018/11/24 21:15:07 */
void main() {
char str[20] = { 0 };
while (1) {
for (char ch = '0'; ch <= '9'; ch++) {
sprintf(str, "color %c%c", ch, 'e');
system(str);
}
}
}
应用案例((Chapter2/datatype/string/string_sprintf_title.c)):经过sprintf函数实现整合字符串实现改变窗口标题
#include <stdio.h>
#include <stdlib.h>
/* 经过sprintf函数实现整合字符串实现改变窗口标题 @author tony ittimeline@163.com @version 2018/11/24 21:52:07 */
void main() {
char str[100] = { 0 };
sprintf(str, "title power by %s", "tony");
system(str);
}
bool类型只有两个值,即true和fasle,它们在内存中分别使用1和0表示,这样一个字节即可以存储bool类型的变量,应用案例(Chapter2/datatype/bool/bool_value.c)以下所示
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* bool的使用 @author tony ittimeline@163.com @version 2018/11/24 21:53:07 */
void main() {
bool flag = true;
//占据的字节数量为1
printf("bool占据的字节数量是%d\n", sizeof(flag));
//成立的结果为1
printf("bool = %d\n", flag);
flag = false;
//不成立的结果为0
printf("bool = %d\n", flag);
system("pause");
}
bool的应用场景就是用来判断条件表达式是否成立,应用案例(Chapter2/datatype/bool/bool_expression.c)以下所示
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* bool的应用场景就是用来判断表达式是否成立 @author tony ittimeline@163.com @version 2018/11/24 21:55:07 */
void main() {
bool flag = 5 > 30;
if (flag == true) {
printf("条件成立\n");
}
else {
printf("不成立\n");
}
getchar();
}
printf()函数在输出数据时,不会进行数据类型转换,若是想要获取预期的结果,就须要进行强转实现,应用案例(Chapter2/datatype/convert/convert_printf.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* printf()函数与强制类型转换 @author tony ittimeline@163.com @version 2018/11/24 21:56:07 */
void main() {
//由于printf函数不会进行类型转换,因此这里获得一个错误的结果858993459
printf("%d\n", 12.1);
//12.1为浮点类型,这里使用强制类型转换实现转换为整数
printf("%d\n", (int)12.1);
printf("%f\n", 10); //整数按照浮点数解析,获得的结果就是0.000000
printf("%f\n", (float)10); //强制类型转换
getchar();
}
表示范围小的数据和表示范围大的数据在参与运算时,运算结果的类型会自动转换为表示范围大的类型,应用案例(Chapter2/datatype/convert/convert_auto.c)以下所示。
#include <stdio.h>
#include <stdlib.h>
/* 自动类型转换 在进行算术运算时,会发生自动类型转换 表示范围小的值自动转换为表示范围大的变量,保存精度 char->short>int->long->float->double->long double @author tony ittimeline@163.com @version 2018/11/24 21:57:07 */
void main() {
char ch = 'A';
printf("1.0占据的字节数量是%d\n", sizeof(1.0));
printf("字符变量ch+1的字节数量是%d\n", sizeof(ch + 1));
printf("字符变量ch+1.0的字节数量是%d\n", sizeof(ch + 1.0));
getchar();
}
在某些应用场景下须要使用到强制类型转换,例如银行帐户的取整等等,强制类型转换的应用案例(Chapter2/datatype/convert/convert_force.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换 @author tony ittimeline@163.com @version 2018/11/24 22:00:07 */
void main() {
float fl = 10.8;
float flt = 10.3;
int num = (int)fl + flt; //20.3 先把fl强制转换为int类型,而后再和flt相加
printf("num =%d\n", num);
num = (int)(fl + flt);//21 先把fl和flt相加后,强制转换为int
printf("num =%d\n", num);
getchar();
}
而须要注意的是强制类型转换则会损失原有数据的精度,应用案例(Chapter2/datatype/convert/convert_force_precision.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换的案例 @author tony ittimeline@163.com @version 2018/11/24 22:20:07 */
void main() {
//这里发生了自动类型转换
double dbl = 3;
printf("dbl = %f\n",dbl);
//7.8默认是double类型,这里转换为int会损失精度
int num = 7.8;
//printf()函数没有进行数据类型转换
printf("num =%d\n",num);
getchar();
}
可是因为强制类型转换是由CPU的寄存器完成的,强制转换后不会影响原来的变量值,应用案例(Chapter2/datatype/convert/convert_force_value.c)以下所示
#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换不会改变原有的值 @author tony ittimeline@163.com @version 2018/11/24 22:02:07 */
void main() {
double dbl = 4.5;
//赋值运算会执行类型转换,可是为了考虑到软件工程的规范,这里仍是加上强制类型转换,增长代码的阅读性
int num = (int)dbl; //强制类型转换是在CPU内部的寄存器完成的
printf("dbl = %f\nnum =%d", dbl, num);
getchar();
}
在进行强制类型转换时要考虑数据的极限问题,否则会引起数据溢出,应用案例(Chapter2/datatype/conver/over_flow.c)以下所示。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换的溢出问题 @author tony ittimeline@163.com @version 2018/11/24 22:04:07 */
void main() {
int num = 256;
//无符号的char能存储的最大值为255,这里的256超过它的最大表示范围,所以发生数据溢出
unsigned char ch = num;
printf("num =%d \t ch = %u", num, ch);
getchar();
}
当在进行数据类型转换时,若是该数据是有符号的,在进行数据类型转换时按照符号位数来填充,若是是无符号则按照0来填充,应用案例(Chapter2/datatype/convert/convert.c)以下所示
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 数据类型转换的内存原理 有符号 低字节转高字节按照符号位数填充 无符号 低字节转高字节按照0填充 @author tony ittimeline@163.com @version 2018/11/24 21:57:07 */
void main() {
//正数按照0填充
char ch = 1; // 二进制表示 0000 0001
int num = 1; // 二进制表示 0000 0000 0000 0000 0000 0000 0000 0001
//负数按照1填充
// 二进制表示 原码 1000 0001 反码 1111 1110 补码 1111 1111 ->ff
ch = -1;
// 二进制表示 原码 1000 0000 0000 0000 0000 0000 0000 00001
// 反码 1111 1111 1111 1111 1111 1111 1111 1110
// 补码 1111 1111 1111 1111 1111 1111 1111 1111 -> ffffffff
num = ch;
unsigned char data = 255 + 1; // 二进制补码 1 0000 0000 可是char只能占据8位,所以这里会截取8位即0000 0000,结果位0
printf("unsigned char data的地址是%p", &data);
printf("data = %d", data);
unsigned int u_num = -1; //赋值错误,能编译不意味着结果正确
// 1000 0000 0000 0000 0000 0000 0000 0000 0001
// 1111 1111 1111 1111 1111 1111 1111 1111 1110
// 1111 1111 1111 1111 1111 1111 1111 1111 1111 无符号解析结果为2的32次方即4294967295
for (int i = 0; i < u_num; i++) {
system("mspaint");
}
getchar();
}
应用案例(Chapter2/datatype/app/convert_get_money.c)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 实现偷钱程序 若是帐户余额的分大于等于4分就不偷钱,小于等于3分就偷走 @author tony ittimeline@163.com @version 2018/11/24 22:05:07 */
void main() {
printf("请输入你的帐户余额\n");
double balance = 0.0;
scanf("%lf", &balance);
// 12.34*10=123.4 123.4+0.6=124 124/10.0=12.4 12.4>12.34
double rest = (int)((balance * 10) + 0.6) / 10.0;
printf("rest = %f", rest);
if (rest < balance) {
//
printf("能够偷钱%.2f元", balance - rest);
}
getchar();
}
应用案例(Chapter2/datatype/app/convert_data.c)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 实现对小数点后三位实现四舍五入 @author tony ittimeline@163.com @version 2018/11/24 22:03:07 */
void main() {
printf("请输入四舍五入的三位小数\n");
double input = 0.0;
scanf("%lf", &input);
double val = 1.235;
//1.234*100=123.4 123.4+0.5=123 123/100.0=1.23
// 1.235*100=123.5 123.5+0.5=124 124/100=1.24
// 1.24>1.235
// 1.24-1.235=0.05
//1.235+0.05=1.24
double result = (int)(input * 100 + 0.5) / 100.0;
printf("result =%.2f", result);
getchar();
}