1、概述
C標准中有一個默認參數提升(default argument promotions)規則。
默認參數提升有時會給我們帶來疑惑。本文結合C語言的可變長參數函數來說明默認參數提升存在的陷阱。
2、默認參數提升的定義
標准中的定義如下:
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. -- C11 6.5.2.2 Function calls (6)
意思大概是:如果一個函數的形參類型未知, 例如使用了Old Style C風格的函數聲明,或者函數的參數列表中有 ...,那麼調用函數時要對相應的實參做Integer Pormotion,此外,相應的實參如果是float型的也要被提升為double類型,這條規則稱為Default Argument Promotion。
3、可變長參數函數
熟悉C的人都知道,C語言支持可變參長數函數(Variable Argument Functions),即參數的個數可以是不定個,在函數定義的時候用(...)表示,比如我們常用的printf()\execl函數等;printf函數的原型如下:
int printf(const char *format, ...);
注意,采用這種形式定義的可變參數函數,至少需要一個普通的形參,比如上面代碼中的*format,後面的省略號是函數原型的一部分。
C語言定義了一系列宏來完成可變參數函數參數的讀取和使用:宏va_start、va_arg和va_end;在ANSI C標准下,這些宏定義在stdarg.h中。三個宏的原型如下:
void va_start(va_list ap, last);// 取第一個可變參數(如上述printf中的i)的指針給ap,
// last是函數聲明中的最後一個固定參數(比如printf函數原型中的*fromat);
type va_arg(va_list ap, type); // 返回當前ap指向的可變參數的值,然後ap指向下一個可變參數;
// type表示當前可變參數的類型(支持的類型位int和double);
void va_end(va_list ap); // 將ap置為NULL
當一個函數被定義為可變參數函數時,其函數體內首先要定義一個va_list的結構體類型,這裡沿用原型中的名字,ap。va_start使ap指向第一個可選參數。va_arg返回參數列中的當前參數並使ap指向參數列表中的下一個參數。va_end把ap指針清為NULL。函數體內可以多次遍歷這些參數,但是都必須以va_start開始,並以va_end結尾。
下面是一個具體的示例(摘自wikipedia):
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double tot = 0;
va_start(ap, count); //使va_list指向起始的參數
for(j=0; j<count; j++)
tot+=va_arg(ap, double);//檢索參數,必須按需要指定類型
va_end(ap); //釋放va_list
return tot/count;
}