1. 数据类型数值范围溢出
如标题所述,该错误出现的原因是由于变量的值超出该数据类型取值范围而导致的错误。
例题如下:
(IDE环境:C-Free,编译器为mingw5,如下图)
# include <iostream> int main(){
short int a = ; // short int 取值范围:-32768~32768
short b = ; // short 是short int 的缩写
int c = a + b; // int 取值范围:-2147483648 ~2147483648
short d = a + b;
std::cout << c << ' ' << d << std::endl;
return ;
}
运行结果:
分析原因:a在内存中存储如下,其中第一位是符号位,0表示为正数,1表示负数。
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
那么 a + b 在内存如下:
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
对于 “int c = a + b;” 由于c的取值范围为 -2147483648 ~2147483648,共32位,其中31位数据位,1位符号位,a + b 在c的取值范围内,其符号位依然是0,所以c的取值正常,为32768。
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
反观“short d = a + b;”其取值范围是 -32768~32768,共16位,其中15位数据位,1位符号位,d在内存中内容如下,其符号位为1,表示为负数,又因为数据在内存是以补码存储的,正数的原码反码补码相同,复数的补码是其反码加1,复数的补码的补码就是复数的原码。所以该内存存储形式的反码为 1111 1111 1111 1111,原码 1000 0000 0000 0000,换成10进制-32768(而不是-0).
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
为了验证上述说明的正确性,在VS2015中重新运行,并打印结果的十六进制形式。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <cstddef> int main() {
short int a = ;
short b = ;
int c = a + b;
short d = a + b;
printf("%d: Dec: %d; Hex: %x; size: %d;\n",c, c, c, sizeof(c));
printf("%d: Dec: %d; Hex: %x; size: %d\n",d, d, d, sizeof(d));
printf("%d: Dec: %d; Hex: %x; size: %d\n", d+, d + , d + , sizeof(d + )); system("pause");
return ;
}
结果如下:
对于c的结果,上述分析是正确的;而对于分析d的结果,似乎存在偏差。上述分析中对于d只有16位,而真正表示成十六进制时,却用到了32位,多分配了一倍的位数用来表示(但因为是short型,实际仍然只占2个字节),形成了向最高位借1(最低位是第0位).而为什么会形成这种问题,而不是上述分析那样,暂时不明白(手动标红,待解决)。(已解决,详情查看“【C++】常见易犯错误之数值类型取值溢出与截断(2)”)为了验证编译器为解决这种取值范围溢出,自动多分配1倍内存来表示,做下面实验。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <iostream>
#include <cstddef> int main() {
system("color 3f");
short int a = ;
short b = ;
int c = a + b;
short d = a + b;
printf("%d: Dec: %d; Hex: %x; size: %d;\n",c, c, c, sizeof(c));
printf("%d: Dec: %d; Hex: %x; size: %d\n",d, d, d, sizeof(d));
printf("%d: Dec: %d; Hex: %x; size: %d\n", d+, d + , d + , sizeof(d + )); std::cout << "\n" << std::endl;
char f = ;
char g = ;
char h = f + g;
printf("%d: Dec: %d; Hex: %x; size: %d\n\n", h, h, h, sizeof(h));
system("pause");
return ;
}
结果:
char型取值范围-128~127,编译器为了解决这个问题,并非多分配一倍,在这里多分配了三倍内存表示,即内存中同样共用了32位来表示,但实际只占有1个字节。这是为什么呢,待解决,标红!
注:网上有这样一种负数的补码计算方式:模 - 该数的绝对值【1】
例:-1补码:1 0000 0000 - 0000 0001 = 1111 1111;
-3补码:1 0000 0000 - 0000 0011 = 1111 1101;
2. 数据类型数值范围截断
发生截断的例子参考这里第(6)
参考文献
【1】-128在内存中如何存储
https://blog.csdn.net/magiclyj/article/details/75258195