使用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_tC 實作中的等的特定定義;它們在其他呼叫中可能不匹配),因此它們的值被正確列印。
uj5u.com熱心網友回復:
讓我們觀察這個特定的片段
long a = INT32_MIN;
printf("%ld\n", a); // #1
printf("%ld\n", INT32_MIN); // #2
它列印以下內容(根據您的示例,我自己還沒有嘗試過,它取決于實作,我稍后會注意到):
-2147483648
2147483648
現在,在#1 中,我們傳遞了一個longto 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中的佇列中取出最小的元素
