2017-08-03 136 views
2

我一直在用c編程一段時間。但從來沒有使用整數環繞的程序。我知道如果整數分配4個字節,那麼整數範圍變爲-2,147,483,648至2,147,483,647。如果我們超過了限制,它只是環繞一下。在c中包含整數的部分

我正在使用下面的程序來了解如何環繞發生。

#include <stdio.h> 

int main() { 
int n = 4, s = 2; 

for (int i = 0; i < n; ++i) 
{ 
    for (int j = 0; j < n; ++j) 
    { 
     for (int k = 0; k < n; ++k) 
     { 
      s = 2 * s + 1; 

     } 
    } 
} 
printf("%d\n", s); 
return 0; 
} 

我使用gdb來找出變量s所採用的值。我發現當第30次執行最內層循環時,s的值變爲負值,即-1073741825。然後下一次迭代變成2147483647,第32次迭代變成-1。 The snapshot of gdb

然後它永遠保持爲-1。我懷疑爲什麼在值變爲-1後沒有發生纏繞。我知道二進制中s的值將全部爲1或FFFFFFFF(十六進制)。它永遠不會改變(內部它正在更新,但我們只能看到最後32位,所以它是-1)。但這次環繞不會進入畫面嗎?它依賴於編譯器嗎?或者,gcc是否只允許環繞一次? 任何形式的幫助,將不勝感激。謝謝

+1

簡短回答:適當的環繞保證*只*爲無符號類型。使用簽名類型,可能會發生奇怪的事情,因爲它在技術上是不確定的。 –

+4

它不再更新,因爲'2 *( - 1)+ 1 = -1'。 –

回答

2

嚴格來說,有符號整數的溢出是undefined behavior。然而,在實踐中,大多數實現使用2的補碼錶示來整數,並且環繞將會如何描述。

考慮到這一點,讓我們看看這裏發生了什麼。

隨着循環的進行,最終s將具有值1610612735.到目前爲止,沒有不尋常的事情發生。現在我們乘以2並添加一個。此時結果溢出。我們來看看這些數字的十六進制表示。

1610612735d = 0101 1111 1111 1111 1111 1111 1111 1111 b = 0x5FFFFFFF 
0x5FFFFFFF * 2 = 0xBFFFFFFE 
0xBFFFFFFE + 1 = 0xBFFFFFFF 
0xBFFFFFFE = 1011 1111 1111 1111 1111 1111 1111 1111 b = -1073741825d 

從二進制的角度來看,乘以2就等於1。左移此操作移到一個值到符號位,給你一個負值。

隨着下一個操作,再乘以2溢出。這一次的乘法轉變0到符號位而以前有個1,所以簽收再次發生變化:

0xBFFFFFFF * 2 = 0x7FFFFFFE 
0x7FFFFFFE + 1 = 0x7FFFFFFF 
0x7FFFFFFF = 0111 1111 1111 1111 1111 1111 1111 1111 b = 2147483647 

下一次迭代也溢出:

0x7FFFFFFF * 2 = 0xFFFFFFFE 
0x7FFFFFFE + 1 = 0xFFFFFFFF 
0xFFFFFFFF = 1111 1111 1111 1111 1111 1111 1111 1111 b = -1 

現在我們有-1。從現在起,沒有溢出:

-1 * 2 = -2 
-2 + 1 = -1 

這裏是十六進制同樣的事情:

0xFFFFFFFF * 2 = 0xFFFFFFFE 
0xFFFFFFFE + 1 = 0xFFFFFFFF 

正如你可以看到加倍-1和加入1讓你再次-1,所以這就是爲什麼它不斷重複。這也與乘以2是左移1一致。

+0

「大多數實現使用2的補碼錶示整數和環繞將工作如何描述。」意味着2的補充機器**將環繞。儘管這是常見的行爲,但即使是帶補碼機器的兼容編譯器也可能會在各種情況下環繞,因爲它是未定義的行爲。那些討厭的新C編譯器利用該UB優化代碼。 – chux