C语言笔记——数据的存储
数据的存储
数据类型
常见类型:
char 字符型 short 短整型 int 整型 long 长整型 long long 长长整型 float 单精度浮点型 double 双精度浮点型 struct 结构体类型 enum 枚举类型 union 联合类型 void 空类型
整型在内存中的存储
原码、反码、补码
对于正数来说: 原码、反码、补码都是一样的(符号位为0)
对于负数来说:
- 原码:二进制数(符号位为1 )
- 反码:原码除符号位外,其余位次按位取反
- 补码:反码+1
如:int -5 原码:1 0101 反码:1 1010 补码:1 1011
在计算机系统中,数值一律用补码来表示和存储。 原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
我们可以看到a,b在内存中存储的形式都是以补码的形式,但仔细看会发现,在实际生活中,a的16进制与内存中存储的顺序不同,这就是由于大端小端的不同而产生的顺序不同。
大端小端:
大端(存储)模式:是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中; 小端(存储)模式:是指数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中;
例: int a=1 0x: 大端:| 00 | 00 | 00 | 01 | 小端 :| 01 | 00 | 00 | 00 |
不论是大端还是小端,传输到网络上的顺序都为转换后的顺序,即网络字节序——大端,有其相应的函数进行转化。
判断当前机器的字节序:
#include <stdio.h> #include <stdbool.h> bool check_mode() { int a = 1; return *(char *)&a == 1; //强转为字符取第一个值,若为1,则是小端存储,否则为大端存储 } int main() { bool flag = check_mode(); if (flag) printf("This is little. "); else printf("This is big. "); }
练习1: 输出结果为?
#include <stdio.h> #include <stdbool.h> //32位小字节序处理器 void main() { union { short k; char i[2]; }*s,a; s = &a; s->i[0] = 0x39; s->i[1] = 0x38; printf("%x ", a.k); }
结果:3839(高地址——>低地址)
练习2:
#include <stdio.h> int main() { char a=-128; printf("%u ",a); return 0; }
结果:4294967168 (无符号整型 1111 1111 1111 1111 1111 1111 1000 0000)
练习3:
#include <stdio.h> void main() { unsigned int i; for(i=9;i>=0;i--) { printf("%u ",i); } }
结果:循环输出
…
浮点型在内存中的存储
常见浮点类型:float 、double 、long double。
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
-
(-1)^S * M * 2^E (-1)^s表示符号位,当s=0,V为正数;当 s=1,V为负数。 M表示有效数字,大于等于1,小于2。 2^E表示指数位。
例如:float f = 10.125; 1010.001 --> 1.010001
(-1)^S * M * 2^E S=0 , M=1.010001 , E=3
单精度浮点数存储(32): 双精度浮点数存储(64):
对于上述例子中 f=10.125 如图所示:
若E为8位,它的取值范围为0~ 255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
因此,对于f来说,S=0,E=3 +127=130–> 1000 0010 ,M补齐32位。
练习:
float f = 12.5;
12.5—> 1100.1—>1.1001 S=0; E=3; M=1.1001; 0 1000 0010 1001 0000 0000 0000 0000 000 即内存中存储形式为:41 48 00 00