2016-10-29 160 views
1

可變參數函數如何處理數值常量?例如考慮下面的代碼:可變參數函數和常量

myfunc(5, 0, 1, 2, 3, 4); 

功能看起來是這樣的:現在

void myfunc(int count, ...) 
{ 
} 

,爲了遍歷一個論點va_arg,我需要知道它們的大小,例如int,short,char,float等等。但是,我應該假定數字常量的大小,就像我在上面的代碼中使用的那樣?

試驗表明,剛剛假設int他們似乎這樣編譯器似乎把它們壓int即使這些常量也可以在一個單一的charshort每個代表工作的罰款。

不過,我正在尋找我看到的行爲的解釋。 C中用於將數字常量傳遞給可變參數函數的標準類型是什麼?這是明確定義還是依賴於編譯器? 32位和64位架構有什麼不同?

謝謝!

回答

3

我喜歡Jonathan Leffler's answer,但我想我會提供一些技術細節,對於那些打算編寫可移植庫或者提供帶有可變參數函數的API的人,因此需要深入細節。

可變參數參數如有default argument promotions(C11草案N1570爲PDF;節6.5.2.2函數調用,第6段):

..在每個參數上執行整數升級,並且參數 具有float類型的參數將被提升爲double。這些被稱爲默認參數促銷

[IF] ..類型的升級後的參數不是與升級後​​的參數兼容,則行爲是不確定的,除了以下情況:

  • 一個提升的類型是另一個提升類型是相應的無符號整數類型,並且該值可以在兩種類型中表示;

  • 兩種類型是指針爲字符類型的合格或不合格的版本或空隙

浮點常數double類型的,除非它們與fF(後綴爲1.0f ),在這種情況下,它們的類型爲float

在C99和C11中,整數常量的類型爲int,如果它們適合一個; long(AKA long int)如果它們適合其中一個; long long(AKA long long int)否則。由於許多編譯器假設整數常量沒有大小後綴是人爲錯誤或拼寫錯誤,因此如果整型常量不是int類型,則始終包含後綴是一種很好的做法。

整型常量也可以有一個字母后綴來表示其類型:

  • uUunsigned int

  • lLlong int

  • luulLUULlULuuLUlunsigned long int

  • llLLLllLlong long int

  • lluLLU(或ULL或任何它們的大寫或小寫的變體),用於unsigned long long int

The integer promotion規則在6.3.1.1節中。

總結爲C11的默認參數提升規則(也有一些補充相比,C89和C99,但無顯著變化):

  • float被晉升爲double

  • 所有整數類型其值可以用int表示,被提升爲int。 (這包括無符號和簽署charshort,和類型_Boolint位字段,和更小的unsigned int位字段。)

  • 所有整數類型,它們的值可以由unsigned int來表示(但不是一個int )晉升爲unsigned int。 (這包括不能由int(的CHAR_BIT * sizeof (unsigned int)的位來表示的,換句話說unsigned int位字段),以及unsigned int typedef定義的別名,但僅此而已,我想。)

  • 整數類型至少一樣大因爲int不變。這包括例如類型long/long int,long long/long long intsize_t

中有一條規則「疑難雜症」,我想指出:「符號到無符號是好的,無符號要籤的是前途未卜」

  • 如果自變量被提升爲有符號整數類型,但函數使用相應的無符號整數類型獲取該值,該函數使用模算術獲得正確的值。

    也就是說,負值將會像增加(1 +無符號整數類型中的最大可表示值)一樣,使它們爲正值。

  • 如果將參數提升爲無符號整數類型,但函數使用相應的有符號整數類型獲取值,並且該值可在兩者中表示,則函數將獲得正確的值。如果這兩個值都不能表示,則行爲是實現定義的。實際上,幾乎所有的體系結構都與上述相反,即獲得的有符號整數值與(1 +無符號整數類型的最大可表示值)相減的無符號值匹配。我聽說有些奇怪的可能表示整數溢出或類似的奇怪,但我從來沒有得到這樣的機器我的手套。

man 3 printf手冊頁(Linux手冊頁項目提供)是相當翔實的,如果你比較上述規則的printf說明符。最後的示例函數make_message()(對於vsnprintf()所需的C99,C11或POSIX)也應該很有趣。

+0

感謝您的答案!我在這裏推薦了一個後續:http:// stackoverflow。COM /問題/ 40330749 /爲什麼 - 不 - 我 - 可變參數函數工作與 - 既-INT-和長隆 – Andreas

1

當你寫1,那是一個int常數。沒有其他類型的編譯器允許使用。如果對於需要不同類型的函數有一個非variadic原型,編譯器會將整數1轉換爲適當的類型,但是它自己的1是一個int常數。所以,在你的例子中,所有6個參數都是int

在被調用的可變參數函數處理它們之前,您必須知道某種參數的類型。通過printf()函數系列,格式字符串告訴它期望什麼;類似於scanf()功能家族。

請注意,默認轉換適用於與可變參數函數的省略號相對應的參數。例如,給定:

char c = '\007'; 
short s = 0xB0hD; 
float f = 3.1415927; 

一個電話:

int variadic_function(const char *, ...); 

使用:

int rc = variadic_function("c s f", c, s, f); 

實際上都轉化和csint,並fdouble

+0

感謝您的答案!我在這裏justed一個後續:http://stackoverflow.com/questions/40330749/why-does-my-variadic-function-work-with-both-int-and-long-long – Andreas