2011-10-04 27 views
19

請參閱我的代碼:什麼是stdint.h中這個神祕的宏加號?

#include <stdint.h> 

int main(int argc, char *argv[]) 
{ 
unsigned char s = 0xffU; 
char ch = 0xff; 
int val = 78; 
((int8_t) + (78)); /*what does this mean*/ 

INT8_C(val); /*equivalent to above*/ 

signed char + 78; /*not allowed*/ 

return 0; 
} 

我發現,在<stdint.h>宏定義是:

#define INT8_C(val) ((int8_t) + (val)) 

什麼是這個加號的意思和意義?

+1

應該指出,這個'INT8_C'的定義是錯誤的:7.18。4段落3:「每次調用其中一個宏(注意:指的是INTN_C)應擴展爲一個整型常量表達式**,適用於'#if'預處理指令。**」該類型轉換不起作用在預處理器指令中,因爲在預處理期間不存在類型(因此類型轉換)。你在使用什麼編譯器/平臺?你應該考慮提交關於這個的錯誤報告。 –

+4

@ChrisLutz:在C99中有效;在納入N1256的技術勘誤1中變得無效。這是對[DR#209]的迴應(http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_209.htm)。 –

+0

@凱瑟湯普森 - 啊。我有一份TC2的副本,直到最近還沒有真正意識到技術勘誤。 –

回答

27

的片段:

((int8_t) + (78)); 

表達,一個取值78,應用一元+,然後投射該投擲它到int8_t類型,前遠。這是沒有真正不同的法律表述:

42; 
a + 1; 

也計算表達式,然後扔掉的結果(儘管這些都將可能被優化掉的存在,如果編譯器可以告訴大家,有沒有副作用)。

這些「裸體」表達式在C語言中是完全有效的,並且通常僅當它們有副作用時纔有用,例如i++,它計算i並將其丟棄並帶來副作用,即增加值。

應該使用宏觀的方法是沿的線條更:

int8_t varname = INT8_C (somevalue); 

的原因看似多餘的一元+操作員可在標準中找到。引用C99 6.5.3.3 Unary arithmetic operators /1

一元+或 - 運算符的操作數應具有算術類型;

而且,在6.2.5 Types, /18

整數和浮點類型統稱爲算術類型。

換句話說,一元+阻止您使用宏中的所有其他數據類型,例如指針,複數或結構。

最後,原因你:

signed char + 78; 

片段不工作是因爲它是不一樣的事情。這個人開始聲明signed char類型的變量,但是當它到達+時,扼流器因爲不是合法的變量名稱。爲了使其等同於你的工作片段,您可以使用:

(signed char) + 78; 

這是價值+78的鑄造鍵入signed char

而且,按C99 7.8.14 Macros for integer constants /2,你也應該小心那些宏使用非常量,他們不能保證工作:

在這些宏的任何實例的參數應該是未定義的整數常量(如6.4.4.1中定義的 ),其值不超過相應類型的限制。

6.4.4.1簡單地規定了不同的整數格式(十進制/八進制/十六進制)與各種後綴(UULULLLLL和小寫當量,取決於類型)。底線是他們必須是常量而不是變量。

例如,glibc有:

# define INT8_C(c)  c 
# define INT16_C(c)  c 
# define INT32_C(c)  c 
# if __WORDSIZE == 64 
# define INT64_C(c) C## L 
# else 
# define INT64_C(c) C## LL 
# endif 

,這將使你的INT8_C宏工作正常,但文本INT64_C(val)將事先加工成任何valLvalLL,這兩者都不你會想。

+0

實際上,OP爲「INT8_C」提供的定義違反了標準,我讀過。 7.18.4第3段:「對這些宏中的一個的每次調用都應擴展爲適用於'#if'預處理指令的整型常量表達式**。**」INT8_C「的原始定義不能用於一個'#if'指令,因此是不正確的。 –

+3

@Chris:+使它在預處理指令中有效。 'int8_t'擴展爲0,'(0)+(val)'只是'val'。 –

+0

'int8_t'如何展開爲0?我認爲這是一個typedef? o_O –

10

這是一個一元運算符+。它產生操作數的值,但它只能應用於算術類型。這裏的目的是防止在指針類型的表達式上使用INT8_C

但你的聲明表達

INT8_C(val); 

是未定義行爲。要求INT8_C()的參數是「一個不固定的整數常量...,其值不超過相應類型的限制」(N1256 7.18.4)。這些宏的意圖是讓你寫出類似INT64_C(42)的東西,並且如果int64_tlong,則將其擴展爲42L,或者如果int64_t是「long long」等等,則使其擴展爲42LL

但寫:

((int8_t) + (78)); 
在自己的代碼

((int8_t) + (val)); 

是完全合法的。

EDIT

在問題爲INT8_C()定義:

#define INT8_C(val) ((int8_t) + (val)) 

是在C99有效的,但是不符合要求的根據稍後N1256草案,作爲變化的結果在技​​術勘誤表之一。

最初的C99標準,第7.18.4節。1P2表示:

INT *** N * _C **()應擴展到一個符號整數常量與 的SPECI音響ED值,並鍵入int_least *** N * _t **。

N1256改變這對(第3段):

這些宏中的一個的每一次調用應擴展到適用於的#if預處理指令使用的整數 常量表達式。 表達式的類型應與 表達式的相應類型具有相同的類型,該表達式根據 整數式促銷轉換。表達式的值應該是參數 的值。

該更改是針對Defect Report #209作出的。

EDIT2:但請參閱R ..的答案;它實際上在N1256中有效,原因很模糊。

+1

不僅是OP的代碼錯誤,OP的'stdint.h'副本是錯誤的(如果這真的是他的'INT8_C'的定義)。 –

+0

@ChrisLutz:它有什麼問題? –

+0

它違反了7.18.4的第3段。請參閱下面的答案,所以我不會在那個地方繼續發送垃圾郵件。 –

0

這是一個單一的加號。

只有兩件事可能正在做。

  1. 它適用什麼知道作爲通常的算術轉換操作數。這建立了一個通用實型的結果。如果結果即將轉化爲具體的東西,我想不出任何理由強迫這一點。

  2. 它將操作數限制爲定義算術運算符的類型。這似乎是更可能的原因。

18

似乎大家都錯過了這裏的一元加運算符的要點,這是爲了使結果在#if預處理程序指令中有效。鑑於:

#define INT8_C(val) ((int8_t) + (val)) 

的指令:

#define FOO 1 
#if FOO == INT8_C(1) 

爲擴大:

#if 1 == ((0) + (1)) 

,從而達到預期效果。這是由於#if指令中的任何un #define d符號擴展爲0

如果沒有一元加號(成爲#if指令中的二進制加號),則該表達式在#if指令中無效。

+0

這真是奇怪,不幸的是,它的作品。這是否算作混淆C代碼?我認爲''在一種情況下是一元運算符,在另一種情況下是二元運算符,依賴於'(int8_t)'被解釋爲強制轉換或未定義的符號,與模糊處理相接... –

+0

如果你認爲這樣被混淆,不要閱讀tgmath.h .... –

+1

這同時美麗和醜陋。 –

相關問題