2013-10-26 68 views
4

我寫了一個涉及可變參數的小C程序。見下文: -帶可變參數的函數 - 奇怪的輸出

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

double calculateAverage(int num,...) 
{ 
    va_list argumentList; 
    double sum=0; 
    int i; 

    va_start(argumentList,num); 

    for(i = 0; i < num; i++) 
    { 
    sum += va_arg(argumentList,double); 
    } 
    va_end(argumentList); 
    return(sum/num); 
} 

int main() 
{ 
    printf("%f\n",calculateAverage(3,1,2,3)); 
    printf("%f\n",calculateAverage(4,2,4,6,8)); 
    printf("%f\n",calculateAverage(4,2.0,4.0,6.0,8.0)); 
    printf("%f\n",calculateAverage(3,1,2,3)); 
} 

輸出是:

0.000000 
0.000000 
5.000000 
5.333333 

只有calculateAverage(4,2.0,4.0,6.0,8.0)是給預期的輸出,即,當我特別小數點表示它們。

  • 不應該va_arg(argumentList,double)安全地推動數字翻倍?

  • calculateAverage(3,1,2,3)如何在2個不同的地方給出2個結果?我是否在一些「未定義的行爲」領域?如果是,如何?

我使用的是gcc版本4.8.1。

回答

2

不,不應該。用va_arg(),你可以告訴數據應該假定具有什麼格式。關於內部代表性,1.0看起來完全不同於1。如果你拿1作爲double,你會得到完全錯誤的東西。

這是運行你的函數調用時堆棧上會發生什麼:

double calculateAverage(int num,...) 
{ 
    va_list argumentList; 
    double sum=0; 
    int i; 

    va_start(argumentList,num); 

    printf("%p", &num); 
    unsigned char * c = &num; 
    for(i = 0; i < num * sizeof(double) + 4; i++, c++) 
    { 
    printf(" %02x", *c); 
    } 
    printf("\n"); 
    for(i = 0; i < num; i++) 
    { 
    sum += va_arg(argumentList,double); 
    } 
    va_end(argumentList); 
    return(sum/num); 
} 

然後,

printf("%f\n",calculateAverage(3,1,2,3)); 

0xbfc507d0 03 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 ... 
-0.054776 

printf("%f\n",calculateAverage(3,1.0,2.0,3.0)); 

給出

0xbfd15290 03 00 00 00 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 00 40 00 00 00 00 00 00 08 40 
2.000000 

因爲整數1內部看起來像00 00 00 011.0內部看起來像00 00 00 00 00 00 f0 3f(均爲小端機器上)。

並且將堆棧內容(例如00 00 00 01 00 00 00 02)解釋爲double會導致奇怪的結果。

2

問題是,這種說法sum += va_arg(argumentList,double);

既然你正在試圖解釋爲double - 將與Integer literals發生的問題。將您的int s投射到double,它應該可以正常工作。

printf("%f\n",calculateAverage(3,(double)1,(double)2,(double)3)); 
printf("%f\n",calculateAverage(4,(double)2,(double)4,(double)6,(double)8)); 
printf("%f\n",calculateAverage(4,2.0,4.0,6.0,8.0)); 
printf("%f\n",calculateAverage(3,(double)1,(double)2,(double)3)); 

va_arg不能確定實際類型傳遞給函數的參數的,但使用任何類型爲類型宏參數作爲其類型傳遞。

+1

或者,您可以將小數位添加到數字以提高可讀性,而不是具有多個類型轉換。例如3 - > 3.0 –

+0

@PatTeen - 這將是短暫而甜蜜的是的。 – Sadique

2

的參數晉升爲可變參數函數遵循正常規則:類型比intcharshort等)更小的被提升到int; float被晉升爲double,但int不會被晉升爲double,所以這個

va_arg(argumentList,double) 

告訴編譯器參數是double型的,但在一些電話,他們都沒有。請注意,您可以通過float,因爲它們將被提升爲double

calculateAverage(4,2.0f,4.0f,6.0f,8.0f); 

解決方法是,確保你傳遞double參數,或使用parmN不同的,例如,格式字符串。 (如printf之一)