1. 數據類型數值范圍溢出
如標題所述,該錯誤出現的原因是由於變量的值超出該數據類型取值范圍而導致的錯誤。
例題如下:
(IDE環境:C-Free,編譯器為mingw5,如下圖)
# include <iostream> int main(){ short int a = 32766; // short int 取值范圍:-32768~32768 short b = 2; // short 是short int 的縮寫 int c = a + b; // int 取值范圍:-2147483648 ~2147483648 short d = a + b; std::cout << c << ' ' << d << std::endl; return 0; }
運行結果:
分析原因: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 = 32766; short b = 2; 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+1, d + 1, d + 1, sizeof(d + 1)); system("pause"); return 0; }
結果如下:
對於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 = 32766; short b = 2; 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+1, d + 1, d + 1, sizeof(d + 1)); std::cout << "\n" << std::endl; char f = 127; char g = 1; char h = f + g; printf("%d: Dec: %d; Hex: %x; size: %d\n\n", h, h, h, sizeof(h)); system("pause"); return 0; }
結果:
char型取值范圍-128~127,編譯器為了解決這個問題,並非多分配一倍,在這里多分配了三倍內存表示,即內存中同樣共用了32位來表示,但實際只占有1個字節。這是為什么呢,待解決,標紅! (參考思路:short與char類型運算前的整型提升)
注:網上有這樣一種負數的補碼計算方式:模 - 該數的絕對值【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