C语言可变长参数函数与默认参数提升

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_startva_argva_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;
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/b60962e878050aa4d5178d1f8d78b66c.html