【c语言】数据在内存中的存储
一、 大小端字节序
大端字节序:数据的低字节内容存放在内存的高地址处,数据的高字节内容存放在内存的低地址处,对于0x11223344
小端字节序:数据的低字节内容存放在内存的低地址处,数据的高字节内容存放在内存的高地址处,对于0x11223344
- 什么是高字节和低字节
对于十进制数字:1234,4是低位,1是最高位
同理,对于十六进制:0x11223344,44是地址字节位,11是高字节位- 为什么会有大小端?
数据在内存中的存储是以字节为单位的,char类型的变量占8个bit位,1个字节,但除了char类型之外有很多类型的变量大于1个字节,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度⼤于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。
二、练习
char默认是有符号的,usigned char 为无符号字符类型,char类型变量大小为1个字节,8个bit位
对于无符号类型,其范围为:0-255,是一个周期性的循环
对于有符号类型,其范围为:-128~127,也是一个周期性的循环
2.1. 练习一
int main()
{char a= -1;signed char b=-1;unsigned char c=-1;printf("a=%d,b=%d,c=%d",a,b,c);return 0;
}
-1的补码为
11111111 11111111 11111111 11111111
char只能存储8个比特位,为11111111是补码,为-1;
b的值与a一样;
c是无符号,11111111作为补码,对应值为255
2.2. 练习二
int main()
{char a = -128;printf("%u\n",a);return 0;
}
-128
源码为:10000000 00000000 00000000 10000000
反码为:111111111 111111111 111111111 011111111
补码为:111111111 111111111 111111111 10000000
a中存储的为10000000
打印的是无符号整型,前面要补齐0
则补齐后补码为:00000000 00000000 00000000 10000000
反码为补码取反:11111111 11111111 11111111 01111111
源码为反码+1:11111111 11111111 11111111 10000000
结果为一个很大的值:4294967168
2.3. 练习三
int main()
{char a[1000];int i;for(i=0; i<1000; i++){a[i] = -1-i;}printf("%d",strlen(a));return 0;
}
由于a是char类型,只能存储-128~127的范围,因此,循环开始后:
-1,-2,…-127,-128,127,126,125…,2,1,0 由于strlen到\0的位置停止计算,因此a的长度为128+127=255
2.4. 练习四
int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);printf("%x,%x", ptr1[-1], *ptr2);return 0;
}
对于ptr2,数组a在内存中存储的形式为小端存储: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00
00 a为首元素地址,强转成int后,+1就是+1
假如首元素地址为0x12ff40,强制转换成整型后+1后为0x12ff41,再将其转换成int*
后,为地址,那么地址0x12ff40与0x12ff41相差一个字节,因此ptr2向后走了一个字节,其类型为int*,解引用后拿出的是4个字节,因此ptr2指向的内存空间数据为:
00 00 00 02,由于是小端存储,拿出后排列方式为02 00 00 00 按16进制打印出为0x02000000
三、浮点数在内存中的存储方式
任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
例如:
9.5表示成二进制为:1001.1,写成V的形式为:(-1)^0*1.0011*2^3
所以S=0,M=1.0011,E=3
IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M;
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M;
以32位为例:
在存储的过程中,1<M<2,因此默认M的第一位总是1,可以被舍去,只保存小数点后的部分,等到读取的时候再把前面的1加上,这样做的目的是可以节省1个有效数字;对于指数E来说,在存入时候必须加上一个中间值,对于8位的E来说,中间值是127,对于11位的E来说,中间值为1023
以9.5为例:S=0,M=1.0011,E=3,E+127为130,二进制为:10000010
在存入时候,M为00110 00000 00000 00000 000,所以,在存储在内存中的形式为:
0 10000010 00110000000000000000000
浮点数读取的过程:
E不全为0或不全为1:指数E的计算值减去127(或1023),得到真实值,再将有效
数字M前加上第⼀位的1;
E全为0这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,⽽是还为0.xxxxxx的⼩数。这样做是为了表⽰±0,以及接近于0的很⼩的数字;
E全为1这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s)
练习:
int main()
{int n = 9;float *pFloat = (float *)&n;printf("n的值为:%d\n",n);printf("*pFloat的值为:%f\n",*pFloat);*pFloat = 9.0;printf("num的值为:%d\n",n);printf("*pFloat的值为:%f\n",*pFloat);return 0;
}
9在内存中的补码为00000000 00000000 00000000 00001001
上面两个printf函数内:站在pFloat的角度来看,补码中的数据是以浮点数的形式存储的,即S、E、M的方式存储,读出的值为0.00000000,这时候E全为0,表示很小的接近于0的数字
下面两个printf函数内:9是以浮点数的形式存储的,二进制位1001.0,(-1)^0*1.001*2^3
S=0,E=3,M=001
0 10000010 0010000000000000000000
以整数形式拿出时候,是一个很大的数字,以浮点出取出时候,就是9.0