整形在内存中的存储
一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
那数据在所开辟内存中到底是如何存储的呢?
比如: int a =20;
我们知道为 a 分配四个字节的空间。那如何存储?
1.原码、反码、补码
计算机中的整数有三种2进制表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”。
数值位正数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码
反码+1就得到补码。(补码取反+1也可以得到源码)
对于整形来说:数据存放内存中其实存放的是补码。
为什么呢?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
关于数据在内存中的存储,涉及到大小端问题,上期有详细讲解。
2.关于有符号char和无符号char的取值范围探究
signed char(char)
我们知道 char 是一个字节,8个比特位,取值就应该是:
00000000~11111111,有2^8种可能,因为我们这是有符号位的。
所以取值范围不是0~255,我们画个图来方便理解下:
我们将正数与负数的补码都转化为十进制,得到 signed char 的取值范围为-128~127。
为了方便大家理解,大家可以看看下图:
从0开始,一直增加到127,然后就到了负数这一块,由负数的最小值开始依次递增,直到 0,又开始循环。
unsigned char
我们依然可以作图:
无符号 char 没有符号位,所以取值范围是0~255。
3.几道简单(易错)的练习题
题一
#include <stdio.h> // 输出什么? 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; }
输出为:a=-1,b=-1,c=255
为什么是 255 呢?
我们来进行如下分析:
#include <stdio.h> int main() { char a = -1; //-1的32位二进制补码截断后存储到a中 //10000000000000000000000000000001 -1的原码 //11111111111111111111111111111110 -1的反码 //11111111111111111111111111111111 -1的补码 //11111111 - a signed char b = -1; //11111111111111111111111111111111 -1的补码 //11111111 - b unsigned char c = -1; //11111111111111111111111111111111 -1的补码 //11111111 - c printf("a=%d,b=%d,c=%d", a, b, c); //-1的整形提升(有符号char) //11111111111111111111111111111111 //11111111111111111111111111111110 //10000000000000000000000000000001 -1 //-1的整形提升(无符号char) //11111111 注意:无符号数字整形提升高位补0 //00000000000000000000000011111111 255 return 0; }
题二
%u 是打印无符号整形,认为内存中存放的补码对应的是一个无符号数
%d 是打印有符号整形,认为内存中存放的补码对应的是一个有符号数
#include <stdio.h> int main() //输出什么? { char a = -128; printf("%u\n",a); return 0; }
输出:4294967168
分析:
int main() { char a = -128; //10000000000000000000000010000000 //11111111111111111111111101111111 //11111111111111111111111110000000 - 补码 //10000000 - a //11111111111111111111111110000000 4294967168 printf("%u\n", a); return 0; }
假设将 -128 改为 128 呢?
结果不变
题三
#include <stdio.h> int main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%u\n", i); } return 0; }
我们知道无符号整型是大于等于 0 的,所以以上代码为死循环。
题四
int main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); //strlen 找到\0或ASCII为0的元素即停止 return 0; }
输出:255
我们知道 char 型是向内存开辟 1 个字节,也就是 8 个比特位,那么 a [ i ] 的取值范围应该是-128~127,结合题目,数组 a 的元素应该是 {-1,-2,-3,…,-128,127,126,…,2,1,0,-1,-2…}
所以,输出255
总结
在整型数字的存储中,数字都是最先转化为 32 位二进制,再根据其对应类型进行截取;
在打印时根据其对应的要求进行整型提升。