2017-04-14 34 views
3

我正在做一些數學,然後打印符號長整型,像這樣:簽名的長整數變成負數,因爲它增長?

file1.c中

#include <stdio.h> 

int main (int argc, char *argv[]) 
{ 
    long my_signed_integer = 9999L * 9999L * 9999L * 9999L; 
    printf("This is my signed integer %ld.\n", my_signed_integer); 

    return 0; 
} 

的Makefile

CFLAGS=-Wall -g 

all: file1 

clean: 
    rm -f file1 

我是想怎麼看遠遠我可以去沒有我的編譯器給我一個錯誤,每增加一個9999L乘法,然後運行:

make 
./file1.c 

看看會發生什麼。


4倍

當使用9999L 4倍(如上面的例子),我得到:

This is my signed integer 9996000599960001.

沒有任何警告。


5次

使用9999L 5次,我得到1個警告:

warning: overflow in expression; result is 7716289630452291919 with type 'long' 
     [-Winteger-overflow] 

但文件仍然編譯,並且最終結果是:

This is my signed integer 7716289630452291919.


6次

使用9999L 6倍,我得到2個警告 - 一個用負數:

warning: overflow in expression; result is 7716289630452291919 with type 'long' 
     [-Winteger-overflow] 
     long my_signed_integer = 9999L * 9999L * 9999L * 9999L * 9999L * 9999L; 
                  ^
warning: overflow in expression; result is -7550445434587511647 with type 'long' 
     [-Winteger-overflow] 
     long my_signed_integer = 9999L * 9999L * 9999L * 9999L * 9999L * 9999L; 

唉,文件仍然編譯,其結果是:

This is my signed integer -7550445434587511647.


隨着我添加越來越多的整數,這種模式會繼續下去 - 我每次都會收到另一個警告。

首先,有人可以解釋爲什麼編譯器不會崩潰並拒絕編譯文件?顯然有一個溢出 - 爲什麼這是容忍的,其他情況 - 比如乘數非常大的數字 - 會使它崩潰?

另外,爲什麼是最終的結果負整數?

+3

帶符號的整數溢出是未定義的行爲,換句話說,「行爲,使用非國際標準不要求的不可移植或錯誤的程序結構或錯誤數據」。因此,編譯器可以自由地拒絕編譯,可以發出警告,或者可以做任何喜歡的事情,例如編譯一個格式化硬盤的程序(不太可能)。用'-Werror'你會在這種情況下得到一個錯誤而不是警告。 –

+4

爲了擴展@IljaEverilä寫道:沒有要求實現來記錄它如何hebaves,也不會產生相同的行爲的代碼的不同部分。這意味着檢查**具體發生的事情是無用的。你必須解決這個問題。 – Olaf

+1

至於爲什麼在這種特殊情況下溢出會導致負數,您的系統可能會使用[2的補碼錶示](https://en.m.wikipedia.org/wiki/Two%27s_complement),但@Olaf指出你不能依賴這種事情發生。 –

回答

6

溢出符號整數類型是undefined behavior按C標準。所以編譯器可以自由地生成它希望的任何行爲。這也意味着它可以自由生成警告或不生成警告。

在實踐中,使用2對整數補碼錶示的託管實現,溢出將表現爲雖然參數是無符號(因此相比減少模2 ^(比特長度)),並解釋爲簽署的結果。

在你的具體情況下,似乎long是一個64位類型。因此,將9999L自己乘以4次就可以符合該類型,但是又會有溢出。您得到的值是結果的最低64位,解釋爲signed long

在5次的情況下,高位不會被設置,所以結果顯示爲正值。在6次的情況下,高位被設置,所以它顯示爲負數。

+2

現代編譯器可以並且會在任何程度上利用UB,不能保證無符號(儘管它可能只是在**和x86等通用CPU上進行簡單的添加)。對於常量表達式,編譯器可以生成相當粗糙的代碼,但是,因爲它知道UB在運行。 – Olaf