2015-02-07 64 views
7

假設以下幾點:printf格式符號數

sizeof(char) = 1 
sizeof(short) = 2 
sizeof(int) = 4 
sizeof(long) = 8 

printf格式爲一個2字節符號數是%hd,對於4字節的有符號數爲%d,對於8字節符號數是%ld ,但是1字節有符號數字的正確格式是什麼?

+0

僅供參考:'sizeof(char)'在規範中定義爲總是'1',不管'char'的實際大小。 – 2015-02-07 21:57:34

+0

將它提升爲「int」並僅使用「%d」有什麼不妥? – 5gon12eder 2015-02-07 21:57:34

+2

至於你的問題,你可能想閱讀例如[此參考資料](http://en.cppreference.com/w/c/io/fprintf)。 – 2015-02-07 21:58:08

回答

6

1字節有符號數的正確格式是什麼? 。

%hh和您所選擇的整數轉換符(例如,%02hhX見C11標準,§ 7.21.6.1p5:

hh

指定後續diou,xX轉換說明符適用於帶符號的char或unsigned char參數(參數將已根據整數升級進行了升級,但打印前它的值應轉換爲帶符號的char或unsigned char);…

加括號的評論很重要。由於可變參數函數參數的整數提升(如printf),函數從不會看到參數char。許多程序員認爲這意味着沒有必要使用hhh限定符。當然,你不會因爲將它們排除在外而造成不確定的行爲,並且大部分時間都會起作用。

但是,char可能會被簽名,整數升級將保留其值,這將使其成爲一個有符號的整數。使用無符號格式(例如%02X)打印帶符號的整數將顯示符號擴展F。因此,如果您想使用無符號格式顯示帶符號的char,則需要使用hh告訴printf整數類型的原始未擴展寬度。

在的情況下是不明確的,一個簡單的例子(但有爭議)例如:

/* Read the comments thread to this post; I'll remove 
    this note when I edit the outcome of the discussion into 
    the answer 
*/ 

#include <stdio.h> 
int main(void) { 
    char* s = "\u00d1"; /* Ñ */ 
    for (char* p = s; *p; ++p) printf("%02X (%02hhX)\n", *p, *p); 
    return 0; 
} 

輸出:

$ ./a.out 
FFFFFFC3 (C3) 
FFFFFF91 (91) 

在註釋螺紋,存在(或可能是)相當多的討論關於是否上述片段是未定義的行爲,因爲X格式規範需要一個無符號參數,而char參數我(至少在產生輸出結果的實現上)簽名。我認爲這個論點依賴於§ 7.12.6.1/p9:「如果任何參數不是相應轉換規範的正確類型,則行爲是未定義的。」

然而,在char(和short)整數類型的情況下,在參數列表中的表達被晉升爲intunsigned int稱爲函數之前。(值得一提的是,在大多數架構,所有這三個字符類型將被提升到一個簽名int;促進一個unsigned char(或無符號char)的一個unsigned int只會發生在一個實現,其中sizeof(int) == 1。)

所以上大多數體系結構中,將對%hx%hhx格式轉換的參數進行簽名,並且這些不能是未定義的行爲,而不使用這些格式代碼就沒有意義。

此外,標準並沒有說fprintf(和朋友)會以某種方式恢復原始表達。它所說的是,值「將在打印之前將轉換爲帶符號的字符或無符號字符」(§ 7.21.6.1/p5,上面引用,強調增加)。

將有符號值轉換爲無符號值不是未定義的。它甚至沒有指定或實現依賴。它簡單地包括(概念上)「重複地增加或減少一個比在新類型中可以表示的最大值更大的值,直到該值在新類型的範圍內」。 (§ 6.3.1.3/p2)

因此,有一個良好定義的過程來參數表達式轉換爲(可能簽名)int參數,該值轉換爲unsigned char良好定義的過程。因此,我認爲像上面提到的那樣的程序是完全明確的。

對於佐證,的fprintf給定格式說明%c的行爲被定義如下(§ 7.21.6.8/p8),加上強調:

int參數轉換爲unsigned char ,並寫出結果字符。

如果一個人申請這使得上述程序未定義所提出的限制性解釋,那麼我相信一個將被迫還認爲:

void f(char c) { 
    printf("This is a '%c'.\n", c); 
} 

也是UB。然而,我認爲幾乎所有的C程序員都寫了類似的東西,而沒有再三思考。

問題的關鍵部分是§7.12.6.1/ p9(以及§7.12.6.1的其他部分)中「參數」的含義。 C++標準略微更精確;它指定如果參數受默認參數促銷的影響,「參數的值在調用之前轉換爲促銷類型」,我解釋這意味着在考慮呼叫時(例如呼叫fprintf),這些論點現在是推廣價值。

我不認爲C實際上是不同的,至少在意圖。它使用類似「參數&hellips;被提升」的措詞,並且至少在一個地方「推廣後的論點」。此外,在可變參數函數(va_arg宏,§ 7.16.1.1)的描述中,參數類型的約束被括號註釋爲「實際下一個參數的類型(根據默認參數促銷提升)」。

我會自由地認爲,所有這一切都是(a)細讀不夠精確的語言,(b)數舞蹈天使。但我沒有看到任何價值宣稱像使用%cchar參數的標準用法是「技術上」UB;這使UB的概念變得不明確,很難相信這種禁止是有意的,這使我相信這種解釋不是有意的。 (也許應該在編輯上加以糾正。)

+0

「打印前它的值應該轉換爲帶符號的字符還是無符號的字符」的目的是什麼?這個額外的轉換完成了什麼? (另見我在這個問題下給Joachim的評論。) – 2015-02-07 22:10:35

+1

@MikeNakis:我正在編輯這個問題來回答這個問題,希望有人想知道。讓我知道編輯是否有幫助。 – rici 2015-02-07 22:12:55

+0

是的,它有幫助,謝謝! (當然,爲什麼我沒有想到它!) – 2015-02-07 22:13:58