负数的“溢出”如何理解
对于char类型变量,储存范围是-128到127,即-128到-1,0到127,128个数,对应char一字节8位,储存2^8=256个数。
对于有符号数来说,其大小有一个反直觉的地方,即-128_{2}是大于127_{2}的
对于二进制数而言,有以下规律
0000 0000 : 0
0000 0001 : 1
0111 1111 : 127
1000 0000 : -128,相当于127+1,故称第一位是符号位,此种算法保证-128是最小的负数,
1000 0001 :-127,相当于-128加一
那么,-128减一会发生什么呢?
代码示例:
#include<stdio.h>
int main(){
char a,b,c;
//char类型变量储存范围是-128到127,即-128到-1,0到127
//各128个数,对应char一字节8位,储存2^8=256个数
a=127;//a(2)=0111 1111
b=a+1;//b(2)=1000 0000
printf("%d\n",b);//b=-128,即1000 0000是最小负数
printf("%d\n",b-1);//输出-129,发生隐形转换
c=b-1;
printf("%d",c); //输出127
}
输出结果:
-128
-129
127
想要搞清楚为什么127加一等于-128,而-128-1出现两种不同的结果,我们就要弄清楚计算机里面的减法如何发生。
减法
对于计算机来说,减法并非通过我们直观意义上理解的“-1借位”实现的,而是通过不断+(-1)实现的,现在我们以2-1为例看看这件事情是如何发生的。
设有char a=2;char b=-1;
那么我们知道
a = 0000 0010
b= 1111 1111
2-1即为a+b,假如忽略负数的符号位的功能性,将b视为二进制数255,会发生什么呢?
a+b = 257,溢出到第九位,即 1 0000 0001,char类型截断八位以上的数。
写到这里,想必减法规律已经明晰。通过加255的方式,使char类型数超限后截断,减去256,变相达到减一效果。类似地,对于大数,截去的结果是对256取余。
隐式转换
#include<stdio.h>
int main(){
printf("sizeof(1) = %d\n",sizeof(1));
printf("sizeof(1.1) = %d\n",sizeof(1.1));
printf("sizeof(int) = %d\n",sizeof(int));
printf("sizeof(double) = %d\n",sizeof(double));
printf("sizeof(long long int) = %d\n",sizeof(long long int));
printf("sizeof(float) = %d\n",sizeof(float));
return 0;
}
上面的代码出现了直接printf("%d",b-1);
由下可得,在C语言里面,整数会被转换成int,浮点数会被转化成double。
b-1的过程中,b被隐式转换成了int,负数提升时,符号位保留,即从
1000 0000转化成 1111 1111 1000 0000=-128(此处符号位保留,具体规则见“负数,补码的艺术”),此时再对-128-1不会溢出,产生回绕现象,故printf结果为-129
(负数那篇文章我还没写,写了会回来删掉这行)
sizeof(1) = 4
sizeof(1.1) = 8
sizeof(int) = 4
sizeof(double) = 8
sizeof(long long int) = 8
sizeof(float) = 4