目录
- 数据已知类型有
- 内置类型(char,int,float,double等);
- 构造类型(数组、结构体、枚举、联合);
- 指针类型;
- 空类型(void);
- 数据类型的作用,开辟及访问内存大小;
一,类型的基本归类
- 整型家族(unsigned、signed)
- 浮点家族(float、double)
- 构造类型(arry、struct、enum、union)
- 指针类型
- 空类型(void)
//整型家族
//char
unsigned char
signed
//short [int]表示可省略
unsigned short[int]
signed short[int]
//int
unsigned int
signed int
//long
unsigned long[int]
signed long[int]
//浮点家族
float
double
//构造类型
数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
//指针类型
char* p
int* p
float* p
void* p
...等
//空类型
//通常作为函数的返回类型、函数的参数、指针类型
void 表示无类型
void test(void)
二,大小端
大小大于一个字节时,就会存在多个字节安排排序的问题;
- 大小端,即数据内存存储的地址顺序;
- 大端(大端字节序),值数据的低位保存在内存的高地址中,而数据的高位保存内存的低地址;
- 小端(小端字节序),值数据的低位保存在内存的低地址中,而数据的高位保存内存的高地址;
int checkSystem()
{
int num = 1;
//小端内存存储顺序应为 01 00 00 00
//大端内存存储顺序应为 00 00 00 01
char* p = # //解引用时会仅访问1个字节
return *p;
}
int main()
{
if (checkSystem())
printf("小端");
else
printf("大端");
}
//结果:小端
//visual studio 2019为小端编译器
三,整型在内存中的存储
- 创建变量,即会在内存中开辟空间;
- 空间大小,由数据类型决定;
有符号整数(signed)
- 符号位(0/1) + 数值位组成;
- 数据在内存中存放的是二进制补码;
- 正整数,原码、反码、补码相同;
有三种二进制形式(原码、反码、补码):
- 原码,直接按照正负数直接翻译成的二进制;
- 反码,在原码基础上符号位不变,其余取反;
- 补码,原码转化为反码 + 1;
int main()
{
//正整数,原码、反码、补码相同
int a = 10;
//0000 0000 0000 0000 0000 0000 0000 1010 内存补码形式
//0a 00 00 00 小端十六进制形式
//负整数
int b = -10;
//1000 0000 0000 0000 0000 0000 0000 1010 原码
//1111 1111 1111 1111 1111 1111 1111 0101 反码
//1111 1111 1111 1111 1111 1111 1111 0110 补码,反码+1
//f6 ff ff ff 小端十六进制形式
}
使用原反补码原因:
- 计算机中,整数数值一律用补码来表示和存储;
- 使用补码可将符号位和数值域统一处理(即运算);
- 使得加减法也可统一处理(CPU只有加法器);
- 此为补码和原码相互转换其运算过程是相同的,无需额外的硬件电路;
- 可将补码取反+1得到原码;
注:先翻译为补码运算,再还原为原码输出;
补充:
- char,是signed还是unsigned取决于编译器;
- int,即是signed int;
- short,即是signed short;
注:头文件limts.h,可查看取值范围;
//16进制书写,即补码
int a = 0xffffffff; //-1
unsigned int b = 0xffffffff; //4294967295
字符char类型
- 有符号char类型,取值范围为 -128 ~ 127(有符号1000 0000直接翻译为-128);
- 无符号char类型,取值范围为 0 ~ 255;
- char类型超过取值范围,会扣除256,循环取值,即正向越界-256,负向越界+256;
int main()
{
//有符号char类型,-128~127
char a = 200; //实际存储-56
char b = -200; //实际存储56
//无符号char类型,0-255
unsigned char c = 200; //实际存储200
unsigned char d = -200; //实际存储56
printf("%d %d %d %d\n", a, b, c, d);
}
int main()
{
//逻辑顺序为,先将值存入变量内,考虑是否截断
//在将变量的补码元素,此时需考虑整型提升问题
//计算的结果补码,看是否需要转换为原码
char a = 128; //其实a实际值为-128,因为值已越界
char b = 5;
printf("%d %u", a + b, a + b);
//1000 0000 a补码
//0000 0101 b补码
//a+b,先需整型提升
//1111 1111 1111 1111 1111 1111 1000 0000 a提升后补码
//0000 0000 0000 0000 0000 0000 0000 0101 b提升后补码
//1111 1111 1111 1111 1111 1111 1000 0101 a+b后补码
//%d为有符号整数,需按规则转换为原码
//1111 1111 1111 1111 1111 1111 1000 0100 反码
//1000 0000 0000 0000 0000 0000 0111 1011 原码,-123
//%u表示无符号整数,原反补相同,无需转换
//1111 1111 1111 1111 1111 1111 1000 0101 补码,4,294,967,173
}
//结果:-123 4294967173
注:运算时涉及整形提升或算术转换
四,浮点类型在内存中的存储
头文件float.h,可查看取值范围;
浮点类型的数据存储,使用另外一种方式,与整型数据存储完全不一样;
int main()
{
int a = 10;
//0a 00 00 00 a内存十六进制补码形式
float b = 10;
//00 00 20 41 b内存十六进制补码形式
printf("%d %f", a, b); //结果:10 10.000000
}
//同样值,内存存储的形式完全不一样
浮点型内存存储规则
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
- (-1)^S * M * 2^E
- (-1)^S,表示符号位0、1;
- M,表示有效数字,大于等于1,小于2;
- 2^E,表示指数位,即使用科学计数法移位的值;
float a = 10;
//十进制10的二进制为1010,按照规则(-1)^0 * 1.010 * 2^3
//S = 0
//M = 1.010
//E = 3
float b = -5;
//十进制-5的二进制为101,按照规则(-1)^1 * 1.01 * 2^2
//S = 1
//M = 1.01
//E = 2
float c = -0.5;
//十进制-0.5的二进制可类似为-0.101,按照规则(-1)^1 * 1.01 * 2^-1
//S = 1
//M = 1.01
//E = -1
S,M,E内存中的分布
- IEEE754规定,32位浮点数,S(1位),E(8位),M(23位)
- IEEE754规定,64位浮点数,S(1位),E(11位),M(52位);
M (1<=M<2),有特别规定:
- 规定需写为1.******,但默认首位1省去,只写小数点后的部分;
- 读取的时候在添加上1,这样可保存24位有效数字(32位存储);
E (unsigned int,为无符号数)
- 在32位存储为8位,则取值范围为0-255;
- 在64位存储为11位,则取值范围为0-2047;
- 因为E可能会取到负数,所以实际上规定要加个中间数,即+127或+1023;
//32位存储
float a = 10;
//十进制10的二进制为1010,按照规则(-1)^0 * 1.010 * 2^3
//S = 0
//M = 1.010 -> 0100 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = 3 -> 1000 0010 (加127,130)
//0100 0001 0010 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//41 20 00 00
//00 00 20 41 小端内存十六进制形式
float b = -5;
//十进制5的二进制为101,按照规则(-1)^1 * 1.01 * 2^2
//S = 1
//M = 1.01 -> 0100 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = 2 -> 1000 0001 (加127,129)
//1100 0000 1010 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//c0 a0 00 00
//00 00 a0 c0 小端内存十六进制形式
float c = -0.5;
//十进制0.5的二进制可类似为0.1(二进制11相对于1*2^1+1*2^0=3, 0.1相对于1*2^-1=0.5)
//按照规则(-1)^1 * 1.0 * 2^-1
//S = 1
//M = 1.0 -> 0000 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = -1 -> 0111 1110 (加127,126)
//1011 1111 0000 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//bf 00 00 00
//00 00 00 bf 小端内存十六进制形式
float d = 0.75;
//十进制0.75的二进制可类似为0.11(二进制11相对于1*2^1+1*2^0=3, 0.11相对于1*2^-1+1*2^-2=0.75)
//按照规则(-1)^0 * 1.1 * 2^-1
//S = 1
//M = 1.1 -> 1000 0000 0000 0000 0000 000 (去除1,从前向后排放,不足补零)
//E = -1 -> 0111 1110 (加127,126)
//0011 1111 0100 0000 0000 0000 0000 0000 (最后实际存储的内存形式)
//3f 40 00 00
//00 00 40 3f 小端内存十六进制形式
浮点型内存还原规则
注:32位为例,二进制内存形式,还原为十进制数值时
- 首位S符号位
- 其后8位,-127为E
- 最后23位,补1为M
E还原时有三种情况
- 一般情况下,E不全为0或1:
- E-127(或E-1023);
- M要在首位添加1;
- E全为0时,即+127后仍为0(原2^-127):
- E=1-127(或1-1023),不在是0-127(或0-1023);
- 且M的有效数字不再加1;
- +/- 0.******的小数,这样做是为了表示±0,其实就是接近于0的很小的数字;
- E全为1时,即+127后为255(原2^128):
- E=255-127=128;
- +/- 1.******,如有效数字M全为0,表示±无穷大(正负取决于符号位s);