簡體   English   中英

C簽名算術:文字和變量之間的表示差異

[英]C signed arithmetic: difference in representation between literals and variables

以下程序:

#include <limits.h>
#include <stdio.h>

int main() {
  long int max = LONG_MAX;
  long int max_plus_one = max + 1;

  printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
  printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
      max_plus_one, max + 1, LONG_MIN);

  printf(" max == LONG_MAX? \t\t %s\n", max == LONG_MAX ? "true" : "false");
  printf(" max_plus_one == LONG_MIN ? \t %s\n",\
      max_plus_one == LONG_MIN ? "true" : "false");
  printf(" max + 1 == max_plus_one? \t %s\n",\
      max + 1 == max_plus_one ? "true" : "false");
  printf(" max + 1 == LONG_MIN? \t\t %s\n", max + 1 == LONG_MIN ? "true" : "false");
}

輸出以下內容:

max            7fffffffffffffff
LONG_MAX       7fffffffffffffff
max_plus_one   8000000000000000
max + 1        8000000000000000
LONG_MIN       8000000000000000

max == LONG_MAX?               true
max_plus_one == LONG_MIN ?     true
max + 1 == max_plus_one?       true
max + 1 == LONG_MIN?           false

為什么表達式(max + 1)不等於LONG_MIN?
那等於什么呢?
如果我嘗試printf max + 1作為表達式,我會得到8000000000000000

真是讓人感到困惑!

- 編輯

正如下面的評論和答案中所討論的那樣,簽名溢出是未定義的,編譯器可以隨心所欲,因此我們沒有任何商業質疑它的奇怪之處。

受到下面評論的啟發,我檢查了我的平台生成的匯編代碼(運行ubuntu的linode上的gcc),發生的事情是編譯器只是在沒有執行實際的相等性檢查的情況下將最后一次相等的結果判定為false(否則會產生真正的價值)

溢出有signed整數類型的行為在C中是未定義的。

您的輸出取決於對max + 1的評估,是該未定義行為的表現形式。 (形式上這意味着您的整個程序未定義。)

請注意, LONG_MIN可能定義為-<value> - 1 ,其中<value>LONG_MAX數字相同。 這避免了2的補碼系統中的任何溢出或參數促銷效應。

正如Bathsheba的答案正確指出的那樣,有符號整數溢出具有未定義的行為。

話雖如此,在大多數實現中,評估LONG_MAX + 1的結果可能是LONG_MIN 打印時沒有看到負值的原因是您打印錯誤。

long int max = LONG_MAX;
long int max_plus_one = max + 1;

printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
       max_plus_one, max + 1, LONG_MIN);

%lx說明符需要unsigned long int類型的參數。 你給它一個類型(signed) long int 特別是,你給它一個負值。

通常,如果將數字參數傳遞給期望不同數值類型的函數,則將轉換該值。 這是可能的,因為編譯器知道函數期望的類型。 但是,對於printf ,期望的類型由格式字符串確定,格式字符串在運行時之前不一定(必須)處理。 它可能會解釋long int對象的內容, 就好像它是unsigned long int類型的對象,但嚴格來說,行為是未定義的。

這是一個更簡單的示例,其中打印的數字設置為LONG_MIN而沒有任何溢出:

#include <stdio.h>
#include <limits.h>
int main(void) {
    unsigned long num = LONG_MIN;
    printf("num = %ld\n", num);
    printf("num = 0x%lx (undefined behavior)\n", num);
}

我系統的輸出是:

num = -9223372036854775808
num = 0x8000000000000000 (undefined behavior)

第二個printf打印的正值並不表示num具有正值,只是它打印錯誤。

如果%lxlongunsigned long的范圍內, 可以使用%lx打印有符號值。 沒有格式說明符將以十六進制格式打印任意簽名值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM