簡體   English   中英

為什么 gcc 給出警告說常量是無符號的,因為它太大了,而它是 __int128 類型並且是有符號的?

[英]Why does gcc give a warning saying that the constant is unsigned because it is too large, when it is of the type __int128 and is signed?

考慮以下代碼:

#include <stdio.h>

int main(void) {
    printf("%llu\n", 18446744073709551615);
    printf("%llu\n", 18446744073709551615ULL);
    return 0;
}

編譯后( gcc -std=c18 ),我收到以下警告:

test1.c: In function ‘main’:
test1.c:4:26: warning: integer constant is so large that it is unsigned
    4 |         printf("%llu\n", 18446744073709551615);
      |                          ^~~~~~~~~~~~~~~~~~~~
test1.c:4:20: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘__int128’ [-Wformat=]
    4 |         printf("%llu\n", 18446744073709551615);
      |                 ~~~^     ~~~~~~~~~~~~~~~~~~~~
      |                    |     |
      |                    |     __int128
      |                    long long unsigned int

C 標准部分 6.4.4.1.5 和 6.4.4.1.6 說:

integer 常量的類型是相應列表中可以表示其值的第一個。

在此處輸入圖像描述

如果一個 integer 常量不能用其列表中的任何類型表示,它可能有一個擴展的 integer 類型,如果擴展的 integer 類型可以表示它的值。 如果常量列表中的所有類型都是有符號的,則擴展的 integer 類型應該是有符號的。 如果常量列表中的所有類型都是無符號的,則擴展的 integer 類型應該是無符號的。 如果列表同時包含有符號和無符號類型,則擴展的 integer 類型可能是有符號或無符號的。 如果 integer 常量不能由其列表中的任何類型表示並且沒有擴展 integer 類型,則 integer 常量沒有類型。

從上面可以清楚地看出,由於ULONG_MAX不能放入intlong intlong long int ,編譯器將嘗試簽名擴展 integer 類型; 由於ULONG_MAX確實適合__int128 ,因此它成為 integer 常量的類型,從第二條警告消息中可以看出。

這都是預期的行為,但我面臨的問題是,顯然__int128是有符號類型,正如 C 標准所預期的那樣。 但是為什么第一條警告消息(“整數常量太大以至於它是無符號的”)說常量被視為無符號? 這對我來說毫無意義,因為根據 6.4.4.1.6 僅檢查帶符號的擴展 integer 類型,那么 integer 常量如何被視為無符號?


為了稍微澄清一下這個問題,我的問題不printf 格式警告是預期的,我只是把它留在那里以表明常量是__int128類型。

考慮代碼:

#include <stdio.h>

int main(void) {
    __int128 a = 18446744073709551615;
    unsigned long long b = 18446744073709551615;
    return 0;
}

編譯它會給出警告:

test2.c: In function ‘main’:
test2.c:4:22: warning: integer constant is so large that it is unsigned
    4 |         __int128 a = 18446744073709551615;
      |                      ^~~~~~~~~~~~~~~~~~~~
test2.c:5:32: warning: integer constant is so large that it is unsigned
    5 |         unsigned long long b = 18446744073709551615;
      |                                ^~~~~~~~~~~~~~~~~~~~

我的問題是,既然常量是__int128類型,為什么編譯器說它是無符號的? 顯然__int128是有符號類型。

gcc(和 clang)給出了一條診斷消息,因此它符合要求,嚴格來說,它不必支持擴展的 integer 類型,並且它給出了某種診斷消息(“warning: bleh”會讓它們同樣符合要求).

然而,這是一個小的編譯器錯誤,因為十進制 integer 常量使用 6.4.4.1 中的引用列表: int然后long然后long long 因此這適用:“如果常量列表中的所有類型都已簽名,則擴展的 integer 類型將被簽名。”

gcc 12.2 的行為也是如此,我們可以從這個演示中看到:

#include <stdio.h>

int main (void)
{
  _Generic(18446744073709551615,
           long long:          puts("long long"),
           unsigned long long: puts("unsigned long long"),
           __int128_t:         puts("(signed) __int128_t"),
           default:            puts("some extended type") );

  typeof(18446744073709551615) x = -1;
  printf("Value: %d Size: %zu\n", (int)x, sizeof(x));
}

Output:

(signed) __int128_t
-1

如果 integer 常量“太大以至於它是無符號的”,那么 _Generic 將打印“unsigned long long”或“某些擴展類型”。 類似地, x在有符號到無符號的轉換過程中會得到一個正值。

結論:gcc 選擇了正確的類型,但警告消息不正確。 它應該說“整數常量太大以至於它被擴展”之類的話。

我猜這條消息是一些來自 C90 的遺留信息,其中擴展的 integer 類型不存在。 使用-std=c90編譯會添加一個額外的警告:

警告:此十進制常量僅在 ISO C90 中是無符號的

看起來這是應該始終顯示的正確警告。 似乎是在從 gnu90 切換到 gnu11 作為 gcc 的默認選項期間發生的一個小錯誤。

暫無
暫無

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

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