使用stdint.h
來自 glibc(gcc SUSE Linux 9.2.1 版,英特爾酷睿 I7 處理器)時,我在INT32_MIN
直接列印時遇到了一個最奇怪的行為:
#include <stdio.h>
#include <stdint.h>
void main(void)
{
printf("%d\n", INT16_MIN);
int a = INT16_MIN;
printf("%d\n", a);
printf("%ld\n", INT32_MIN);
long b = INT32_MIN;
printf("%ld\n", b);
printf("%ld\n", INT64_MIN);
long c = INT64_MIN;
printf("%ld\n", c);
}
輸出:
-32768
-32768
2147483648
-2147483648
-9223372036854775808
-9223372036854775808
此外,如果我嘗試
printf("%ld\n", -INT32_MIN);
我得到相同的結果,但使用 compiler warning: integer overflow in expression '-2147483648' of type 'int' results in '-2147483648' [-Woverflow]
。
并不是說這對任何現有程式都非常糟糕,實際上它看起來很無害,但這是否是舊 printf 中的錯誤?
uj5u.com熱心網友回復:
這是 glibc printf 中的錯誤嗎?
不。
printf("%ld\n", INT32_MIN);
…2147483648
有一種簡單的方法可以做到這一點。函式的第二個整數/指標引數應該在 64 位暫存器 RCX 中傳遞。INT32_MIN
是int
位模式為 0x80000000的 32 位,因為這是 ?2,147,483,648 的二進制補碼模式。在 64 位暫存器中傳遞 32 位值的規則是它在低 32 位中傳遞,高位不被呼叫的例程使用。對于這次呼叫,0x80000000 被放入了低 32 位,而高位恰好被設定為零。
然后printf
檢查格式字串并期望 64 位long
. 所以它在 RCX 中尋找 64 位整數。當然,傳遞64位整數的規則是使用整個暫存器,所以printf
取所有64位,0x0000000080000000。這是 2,147,483,468 的位模式,因此printf
列印2147483648
.
當然,C 標準沒有定義行為,因此可能會發生其他事情,但對于您觀察到的實體中發生的情況,這是一個可能的場景。
printf("%d\n", INT16_MIN);
…-32768
由于int
在您的 C 實作中是 32 位,因此該int16_t
值INT16_MIN
會自動提升int
為函式呼叫,因此這會傳遞一個int
,并%d
期望一個int
,因此沒有不匹配,并且會列印正確的值。
類似地,問題中的其他printf
呼叫具有與轉換規范匹配的引數(給定int16_t
C 實作中的等的特定定義;它們在其他呼叫中可能不匹配),因此它們的值被正確列印。
uj5u.com熱心網友回復:
讓我們觀察這個特定的片段
long a = INT32_MIN;
printf("%ld\n", a); // #1
printf("%ld\n", INT32_MIN); // #2
它列印以下內容(根據您的示例,我自己還沒有嘗試過,它取決于實作,我稍后會注意到):
-2147483648
2147483648
現在,在#1 中,我們傳遞了一個long
to printf
。該函式使用可變引數串列,并根據指定的格式,我們的引數值將被解釋為 a signed long
,因此我們最終得到值-2147483648
。
對于#2,我們的情況有些不同。如果您查看如何INT32_MIN
定義,您會發現它是一個普通的舊 C 宏,例如:
# define INT32_MIN (-2147483647-1)
所以,我們的線路
printf("%ld\n", INT32_MIN);
實際上是
printf("%ld\n", (-2147483647-1));
引數型別不是長整數,而是整數(有關詳細資訊,請參見此處)!對比一下這個
int b = INT32_MIN;
printf("%ld\n", b);
它列印出您觀察到的相同值(正值)。
現在歸結為您的問題實際上應該是什么:
當
printf
接收到不在格式說明符中的型別的值時會發生什么?
未定義的行為!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/361332.html
下一篇:如何從C中的佇列中取出最小的元素