原码、反码、补码
正数符号位为0,负数符号位为1;
对于正数来说,原码=反码=补码;
对于负数来说,反码=原码除符号位以外其余位全取反,补码=反码+1;
在内存中,只要是整数,都按补码来存放;
对于一个负数-10,其补码是1111 1111 1111 1111 1111 1111 1111 0110(假设int为4bytes),继续对其进行除符号位以外其余位取反,再加1,则可以得到-10的原码。
char
0000 0000 = 0
0000 0001 = 1
0000 0010 = 2
…
0111 1111 = 127
1000 0000 = -128
1000 0001 = -127
…
1111 1110 = -2
1111 1111 = -1
signed char:-128~127
unsigned char:0~255
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的原码:1000 0000 0000 0000 0000 0000 0000 0001
-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
char是一个字节,发生截断(截取低地址的八个bit,跟数据是小端存储还是大端存储没关系,都是把数据放出来之前进行截断,在存储的时候才会有大小端的问题),因此,a=1111 1111,a用%d打印,发生整型提升(a有符号的,前面全部补1(按符号位来补)),变为1111 1111 1111 1111 1111 1111 1111 1111,而因为内存中存储的是补码,因此前面的1111 1111 1111 1111 1111 1111 1111 1111是补码,求得其原码为1000 0000 0000 0000 0000 0000 0000 0001,因此打印出来是a= -1;
b同上;
c=1111 1111,但因为c是unsigned的,所以c=1111 1111=255,c用%d打印,发生整型提升(c无符号的,前面补0),变为0000 0000 0000 0000 0000 0000 1111 1111,0000 0000 0000 0000 0000 0000 1111 1111为补码,其原码是还是0000 0000 0000 0000 0000 0000 1111 1111(正数原码=反码=补码),因此打印出来是c=255;
int main()
{
char a[1000];
int i;
for(i = 0; i < 1000; i++) // i=0、1、2...1000
{
a[i] = -1 - i; // a[i] = -1、-2、... -128、127、...、1、0
}
printf("%d", strlen(a)); // 255
}
结果为255;
首先strlen()是通过字符串中’\0’来求字符串的长度;char的取值范围为-128 ~ 127,当a[i]=0时,也就是’\0’,故128+127=255;
大小端
小端字节序存储模式:数据的高位存储在高地址处,低位存储在低地址;
大端字节序存储模式:数据的高位存储在低地址处,低位存储在高地址;
小端与书写顺序相同,大端与书写顺序相反;
注意要看清楚哪边是高地址,哪边是低地址;
X86是小端,c51、很多arm;
浮点数在内存中的存储
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("* pFloat的值为:%f\n", * pFloat);
* pFloat = 9.0;
printf("n的值为:%d\n", n);
printf("* pFloat的值为:%d\n", * pFloat);
最终的结果为9 0.000000 1091567616 9.000000
说明浮点数在内存中的存储与整数的存储方式不同;
任何一个二进制浮点数都可以表示成:
V
=
(
−
1
)
S
∗
M
∗
2
E
V=(-1)^S*M*2^E
V=(−1)S∗M∗2E,
其中S表示符号位,当S=0时,V为正数,当S=1时,V为负数;
M表示有效数字,大于等于1,小于2;
E表示指数;
举例:
V=5.0f == 101.0 == 1.01*2^2 == (-1)^0 * (1.01) * (2^2),因此S=0,M=1.01,E=2;
IEEE 754
IEEE 754对M和E的规定:
存入内存
- 对于M,因为其大于等于1,小于2,因此可以写成1.xxxx(二进制),第一位1可以略去,只保存后面的xxxx;
- 对于E,E为无符号整数,如果E为8位,其取值范围为0-255,如果E为11位,其取值范围为0-2047,但E有可能出现负数,因此,需要把实际的E加上127(对于E为8位的情况),加上1023(对于E为11位的情况)得到最终存入E的数据;
从内存中取出
- E不全为1或不全为0:E减去127(或1023)得到真实值,再将有效数字M前加第一位的1;
- E全为0:E=1-127(或1-1023)得到真实值,有效数字不再加上第一位的1,而是还原为0.xxxx的小数。这样做是为了表示±0,以及接近0的很小的数字;
- E全为1:这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);