2014-07-04 41 views
2

我正在讀這本書Head First C,我是關於可變參數的部分。在帶有可變參數的函數中傳遞「enum」時出錯

我寫了下面的代碼:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdarg.h> 

enum drink { 
    MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE 
}; 

double price(enum drink d) { 
    switch(d) { 
    case MUDSLIDE: 
     return 6.79; 
    case FUZZY_NAVEL: 
     return 5.31; 
    case MONKEY_GLAND: 
     return 4.82; 
    case ZOMBIE: 
     return 5.89; 
    } 
    return 0; 
} 

double calc(int args, ...) { 
    double total = 0; 

    va_list ap; 
    va_start(ap, args); 

    int i; 
    for (i = 0; i < args; i++) { 
     int currentDrink = va_arg(ap, int); 
     total += price((drink) currentDrink); 
    } 

    va_end(ap); 
    return total; 
} 

int main() { 
    printf("Price is %.2f\n", calc(2, MONKEY_GLAND, MUDSLIDE)); 
    return 0; 
} 

代碼編譯和完美的作品。

但是...我的解決方案有兩條不同的路線。

我:

int currentDrink = va_arg(ap, int); 
total += price((drink) currentDrink); 

書:

enum drink currentDrink = va_arg(ap, enum drink); 
total += price(currentDrink); 

我試圖用執行期間在書中提出的解決方案,但錯誤和報告警告:「飲料'在通過'...'時被提升爲'int'

本書在linux上使用gcc編譯器。我在Windows上使用gcc。

問題:我無法編譯本書中提出的代碼的原因是什麼?

編輯 配置錯誤。當時正在使用C++編譯器,正在考慮使用C. 但問題依然存在:爲什麼在C++中會導致執行中的警告和錯誤?

+2

我不能複製你的問題。這本書的版本適合我。你使用的是什麼版本的gcc?運行'gcc --version'。 – ooga

+0

請提供您的編譯器的版本信息。另外,爲了風格和簡單起見,請嘗試將此用於您的枚舉定義:'typedef enum drink {MUDSLIDE,FUZZY_NAVEL,MONKEY_GLAND,ZOMBIE } drink;'。這樣,你可以使用'drink'作爲一種類型,而不必輸入'enum drink'。 – DevNull

+0

@ooga gcc(tdm-1)4.5.2 – Macabeus

回答

4

可變參數函數的參數都受到了所謂的默認參數提升轉換:以轉換等級比的int較小的一切都被提升到int傳遞給可變參數函數之前。而且enum的轉換等級較小(可能表示爲short或您的其他情況)。所以你的被調用者看到的是一個int,並且必須像va_arg那樣獲取它。

C99 7.15.1.1第2頁(重點煤礦)約va_arg

[...]如果沒有實際的一個參數,或者如果類型與實際的下一個參數的類型兼容( 作爲根據默認參數提升)推動,該行爲是未定義的,除了以下情況:

  • 一種類型是一個帶符號的整數類型,其他類型是相應的無符號整數類型,並且該值可以在兩種類型中表示; [0126]關於指針類型]

而且6.7.2.2,第4頁:

每個枚舉類型應與炭,有符號整數類型,或一個無符號整數類型兼容。[...]

因此,va_arg(ap, enum drink)版本沒有定義的行爲(就C標準而言)。至少,如果編譯器未指定始終使用int代替enum s。因此來自gcc的警告。

某些編碼的指導方針說,爲避免enum使用情況完全類型,使用int無處不在,只定義enum常量:

enum { 
    MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE 
}; 

double price(int d); 

HTH

+0

你的答案令我困惑。當你談論「他的版本」時,你的意思是他使用'va_arg(ap,int)'的那個。通過參數提升,傳入的'enum drink'將被提升爲'int',因此這個宏調用將是正確的。在本書的版本中,使用了'va_arg(ap,enum drink)',第6.7.2.2節說''enum drink'與'int'(這是參數的實際類型)是兼容的,因此你不能說這是未定義的行爲。 – Anthales

+0

@Anthales:我做錯了......固定,謝謝。不,根據我的理解,它表示它們不兼容(如果編譯器不爲'enum'使用'int')。 – mafso

+0

我相信你是對的。雖然枚舉常量的類型爲'int',但枚舉類型本身可以根據平臺而只與'char'兼容,因此'va_arg(ap,enum drink)'會拉太少的字節。因此,這本書是錯誤的,你必須做一些像'(枚舉飲料)va_arg(ap,int)'。 – Anthales

相關問題