[英]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
不能放入int
、 long int
和long 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.