由变量溢出所想到的
类型 | 比特(位)数 | 数值范围 | |
[signed] int | 16 | -32768~32767(-2^15 ~ 2^15 – 1) | |
unsigned int | 16 | 0 ~ 65535(0~2^16 - 1) | |
[signed] short int | 16 | -32768~32767(-2^15~2^15 - 1) | |
usigned short int | 16 | 0~65535 | |
long [int] | 32 | -2147483648 ~ 2147483647(-2^31 ~ 2^31 - 1) | |
unsigned long int | 32 | 0 ~ 4294967295(0~2^32 - 1) | |
float |
32 | 有效数字 | 数值范围 |
6~7 | -3.4*10^-38~3.4*10^38 | ||
double | 64 | 15~16 | -1.7*10^-308~1.7*10^308 |
long double | 128 | 18~19 | -1.2*10^-4932~1.2*10^4932 |
[signed] char | 8 |
| -128~127 |
unsigned char | 8 |
| 0~255 |
一个char类型溢出的程序
#include<stdio.h>
int main()
{
chara = -128;
charb;
b= a - 1;
printf("%d\n",b);
return0;
}
编译运行结果:
[root@localhost overflow]# gcc char.c
[root@localhost overflow]# ./a.out
127
由char类型的取值范围可猜想:一个unsigned int型变量a = 65535;那么a – 1 = 0;
检验如下:(如今的计算机unsigned int类型的范围为:0 ~ 2 ^ 32-1)
#include<stdio.h>
int main()
{
unsignedint a = 4294967295;
unsignedint b;
b= a + 1;
printf("%d\n",b);
return0;
}
[root@localhost overflow]# gcc int.c
int.c: 在函数 ‘main’ 中:
int.c:5: 警告:这个小数常量仅 ISO C90 中是无符号的
[root@localhost overflow]# ./a.out
0
[root@localhost overflow]#
此处的警告
再试验一下一个unsigned int b = -1会输出什么效果:
#include<stdio.h>
int main()
{
unsignedint a = -1;
printf("%d\n",a);
return0;
}
输出结果:
[root@localhost overflow]# gcc int.c
[root@localhost overflow]# ./a.out
-1
unsigned b=-1;理论上似乎不该有这样的初始化。printf出来的b=-1
我的推理过程
假设上面初始化合法,我们把-1的补码存储到内存中,因为我们标记内存地址为&b的数为无符号整形,所以断点监视时,监测出一个大数;(这个大数似乎跟内存存在一定的关系,而不仅是单纯的CPU字长)
当调用printf函数时,printf根据取到的数先对符号位进行判断,发现符号位为1,是负数,所以求出取出来的数的补码,得到-1;
假设上面的推理成立,即断点监视和printf有自己一套判断一个数的方法,尽管有些荒谬;
我重定义了个整形变量a并且超出了int的可表示范围发现了个问题:内存中的这个数似乎仅给它标记为int类型数据,并不检查范围,只是简单的计算a的补码,并存储起来,高位按0填充;
断点监视和printf时发现是个正数,直接输出这个值;
本想用第二个初始化推倒上面的假设,却没法得到矛盾。
从上面似乎可以得出:int和unsigned似乎不再是C语言中简单的2个字节了,而已经被CPU字长和内存所影响。(上述推理来自网络)