2017-04-03 58 views
0

有一個問題已經回答變量聲明的具體情況,但對於其他字面常量用途?標準C中需要文字後綴嗎?

例如:

uint64_t a; 
... 
int32_t b = a/1000000000; 

是最後一塊代碼當量到下一個在任何標準的C語言編譯器?

uint64_t a; 
... 
int32_t b = (int32_t)(a/UINT64_C(1000000000)); 

換句話說,是xINTn_C宏需要在所有(假設我們使用顯式強制在隱含一個是錯的情況下)?

EDIT

當編譯器讀取1000000000,是它允許將其存儲爲int在內部表示(丟棄所有溢出位),或者必須存儲它在最高的可能精度(長長),直到它解析整個表達式類型?它是一種實現定義的行爲,還是由標準強制的?

+0

這是不好的編輯您的問題,以便它使現有答案中的問題無效。 –

+0

另外,我很好奇爲什麼你問的是舊的C99標準,而不是「通用C」或C11標準。當然,''和''設施不是C90的一部分,但現在C99也很老舊。 –

+0

@JonathanLeffler我修正了編輯問題。我問的是C99,因爲它是ANSI C之後最廣泛支持的標準,並且是我正在使用的標準。如果所有的編譯器都支持C11,那麼世界會更好,但事實並非如此...... – user3368561

回答

3

你的第二個例子是不是有效的C99,看起來像C++。也許你想要的是演員,即(int32_t)(a/UINT64_C(1000000000))

a/UINT64_C(1000000000)a/1000000000之間的差異?不,他們會以相同的操作結束。但我不認爲這真的是你的問題。

我認爲你的問題歸結到會出現什麼整數文字的類型「10億」是什麼?它會是一個int32_t或int64_t? C99中的答案來自§6.4.4.1第5段:

整數常量的類型是其中可以表示其值的對應列表的第一個。

對於無後綴十進制常量,該列表是intlong intlong long int。因此,第一個文字幾乎肯定會是一個int(取決於一個int的規模,這將可能是32位,併爲此大到足以容納一個十億)。帶有UINT64_C宏的第二個字面值可能是unsigned longunsigned long long,具體取決於平臺。它將是與uint64_t相對應的任何類型。

所以類型的常量是不一樣的。第一個將被簽名,而第二個未簽名。第二個很可能會有更多的「長」,這取決於編譯器的基本int類型的大小。

在您的例子,它使沒有區別的文字有不同的類型,因爲/運營商將需要促進字面來的a類型(因爲a將等於或大於秩比字面在任何情況下) 。這就是爲什麼我不認爲這真的是你的問題。

有關爲何UINT64_C()會的問題,考慮如果文字結果的變化都提升到一個更大的類型的表達式的例子。即,文字的本地類型會發生溢出。

int32_t a = 10; 
uint64_t b = 1000000000 * a; // overflows 32-bits 
uint64_t c = UINT64_C(1000000000) * a; // constant is 64-bit, no overflow 

爲了計算c,編譯器將需要促進auint64_t並執行64位乘法。但是要計算b,編譯器將使用32位乘法,因爲這兩個值都是32位。

在最後一個例子,我們可以使用強制的,而不是宏觀:

uint64_t c = (uint_least64_t)(1000000000) * a; 

這將迫使也是乘法至少爲64位。

爲什麼你會使用宏而不是鑄造文字?一種可能性是因爲小數文字被簽名。假設你想要一個不能表示爲一個有符號值的常量?例如:

uint64_t x = (uint64_t)9888777666555444333; // warning, literal is too large 
uint64_t y = UINT64_C(9888777666555444333); // works 
uint64_t z = (uint64_t)(9888777666555444333U); // also works 

另一種可能性是預處理器表達式。一個強制轉換不是用於#if指令表達式的合法語法。但是UINTxx_C()宏是。

由於宏使用粘貼到文字上的後綴,並且沒有後綴,所以可能會發現UINT16_C(x)和UINT32_C(x)是相同的。這給出了結果(uint_least16_t)(65537) != UINT16_C(65537)。不是人們所期望的。事實上,我很難看到它如何符合C99§7.18.4.1:

宏UINTN_C(值)應擴展爲與uint_leastN_t類型對應的整型常量表達式。

+0

爲了更清楚,我更新了這個問題。 – user3368561

+0

鑑於'uint16_t x;'編譯器可能會針對'x = UINT16_C(65539)發佈診斷;'它們不會針對'x = 65539;'發佈?後者會定義行爲(將x設爲3),但在某些情況下,診斷可能更有意義。 – supercat

+0

'UINT16_C(65539)'在65539被簽名時是無符號的,所以可以得到一個隱式的帶符號到無符號轉換診斷。更相關的是將字面量隱式轉換爲uint16_t的溢出,無論哪種情況,都會得到這種溢出。它不會與'x =(uint16_t)65539;'一起生成,因爲轉換不再隱含。 – TrentP