2015-01-20 118 views
3

我有以下函數將傳遞的參數寫入二進制文件。va_arg中的字符類型

void writeFile(FILE *fp, const int numOfChars, ...) 
{ 
    va_list ap; 
    va_start(ap, numOfChars); 
    for(int i = 0; i < numOfChars; i++) 
    { 
     const char c = va_arg(ap, char); 
     putc(c, fp); 
    } 
    va_end(ap); 
} 

編譯後,我從編譯器

warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg 
     has undefined behavior because arguments will be promoted to 'int' [- Wvarargs] 

收到以下警告現在,據我所知,C要推動char類型爲int。爲什麼C想要這樣做?其次,是將int轉換回char的最佳解決方案嗎?

回答

6

現在據我所知,C想要將char類型提升爲int。爲什麼C想要這樣做?

因爲這就是標準所說的。如果將轉換等級小於int(例如char,boolshort)的整數值傳遞給具有可變數量參數的函數,它將轉換爲int。這可能是因爲它的原因源於它的表現,現在(事實上,現在通常還是現在)通過與機器字邊界對齊的值更好。

其次,是將int轉換回char的最佳解決方案嗎?

是的,但你並不真的需要一個投連,隱式轉換會做:

char ch = va_arg(ap, int); 
+0

不需要強制轉換或隱式轉換。查看我的(更新的)答案的結尾。 – 2015-01-21 15:55:14

+0

@KeithThompson你是什麼意思,不需要隱式轉換?因爲它沒有定義,所以你不能寫'char ch = va_arg(ap,char)'?如果你想從參數列表中取出一個'char',你必須將'int'傳遞給'va_arg',這是不是正確的? – 2015-01-21 18:05:22

+0

我的意思是正確的代碼是'int c = va_arg(ap,int); putc(c,fp);'。你可以讓'c'成爲'char',但是由於'putc()'接受了'int'的參數,所以沒有太多意義。 – 2015-01-21 18:13:45

5

可變參數的功能被特殊處理。

對於非變量函數,原型(聲明)指定了所有參數的類型。參數可以是任何(非數組,非函數)類型 - 包括窄於int的類型。

對於可變參數函數,編譯器不知道對應於, ...的參數的類型。由於歷史原因,並且爲了使編譯器的工作更容易,將類型窄於int的任何相應參數提升爲intunsigned int,並且將類型float的任何參數提升爲double。 (這就是爲什麼printf使用相同的格式說明要麼floatdouble參數。)

所以一個可變參數函數不能接收char參數。您可以使用char參數調用此函數,但它將被提升爲int

(在C的早期版本中,引入了原型之前,所有功能表現這種方式,即使C11允許非原型聲明,在狹窄的參數被晉升爲intunsigned int,或double。但考慮到存在的原型,實際上沒有理由編寫依賴於此類促銷的代碼 - 除了可變參數函數的特例)。

因此,va_arg()接受char作爲類型參數沒有意義。

但是語言不是禁止這樣的調用va_arg();實際上描述<stdarg.h>的標準部分沒有提到參數提升。該規則在函數調用部分陳述,N1570 6.5.2.2段7:

如果表示所調用的函數的表達具有類型 確實包括一個原型,參數被隱式轉換,如 如果通過賦值,對應於相應參數的類型,則將參數 的每個參數的類型作爲其 聲明類型的非限定版本。聲明符函數原型 中的省略號表示會導致參數類型轉換在最後一個 聲明的參數後停止。尾部參數在 上執行默認參數促銷。

「默認參數提升」 轉換窄參數intunsigned int,或double。 (無符號整數類型,其最大值超過INT_MAX將晉升爲unsigned int的說法。這是理論上的可能char表現這種方式,但只有在一個非常不尋常的實現。)

其次,是最好的解決辦法將int轉換回char?

不,不是在這種情況下。演員幾乎不需要;在大多數情況下,隱式轉換可以完成相同的工作。在這個特定的情況下:

const char c = va_arg(ap, char); 
putc(c, fp); 

的第一個參數putc已經int類型的,所以這是較好寫爲:

const int c = va_arg(ap, int); 
putc(c, fp); 

int值由putc轉換爲unsigned char並寫入fp

+0

謝謝基思。我每天都不斷地學習關於C的新東西。 :) – Aman 2015-01-20 20:25:54

+0

「但語言不_forbid_ [...]」 - 我迷失在這裏。它明確地未定義:7.16.1.1('va_arg')p2「[I] f _type_與實際下一個參數的類型不兼容(根據默認參數促銷提升),行爲是未定義的,除了以下案例:[...]「 – mafso 2015-01-21 11:04:22

+0

@mafso:*未定義的行爲*並不意味着它被禁止。 'va_arg(ap,char)'既不是語法錯誤也不是違反約束;一致性編譯器不需要拒絕或診斷它。 – 2015-01-21 15:49:38