天天看点

C语言不定参数的作用,不定参数在C语言中的应用实例

不定参数在C语言中的应用实例:不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多。除了格式化输出之外,我实在没看到多少应用。主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替换它。尽管如此,既然大家对它比较感兴趣,我就简单总结一下它的使用和需要注意的常见问题。

刚学C语言的时候,一般人都会首先接触printf函数。通过这个函数,你可以打印不定个数的变量到屏幕,如:

printf("%d", 3);

printf("%d,%d",3,4);

上述代码看似简单,实际上却需要我们解决许多问题。在我们设计printf的时候,我们是不知道到底会传入几个参数的。在这种未知的情况下,我们需要解决下面几个问题:

怎么告诉printf我们会传入几个参数

printf怎么去访问这些参数

函数调用完成后,系统怎么把参数从传递用的堆栈中释放

为了解决这些问题,我们首先要解释cdecl调用约定,所有使用不定参数的函数必须是使用cdecl(全局函数)或者this

call(类成员函数)调用约定。该约定对于参数传递规定如下:

参数从右向左入栈(也就是如果你调用f(a,b,c),则c先入栈,然后是b,最后是a入栈)

调用者负责清理堆栈

其中第二点直接解决了前面三个问题中的第三个问题。我们来详细说说其他两个问题。

确定参数的个数

在一个函数中,一般有如下prelog代码:

00401020 push ebp

00401021 mov ebp,esp

00401023 sub esp,48h

执行上述代码之后,func(a,b,c)函数所处的堆栈上下文就变成如下布局:

C语言不定参数的作用,不定参数在C语言中的应用实例

其中,ebp指向保存旧的ebp的堆栈内存的下一个字的地址,ebp+8指向eip地址,ebp+12则指向函数调用的第一个参数,而ebp和esp之间是用于临时变量(也就是堆栈变量)的空间。

注意,由于上述prelog代码的存在,我们很容易通过ebp得到第一个参数的地址,对于不定参数列表之前的类型固定的参数,我们也可以根据类型信息得到其实际的位置(例如,第一个参数的位置偏移第一个参数的大小,就是第二个参数的地址)。

注意不定参数函数有个限制,就是不定参数的列表必须在整个函数的参数列表的最后。我们不可以定义如下的函数:

void func(int a, ..., int c)

所有类型固定的参数都必须出现在参数列表的开始。这样根据前面的论述,我们就可以得到所有类型固定的参数。

在设计具有不定参数列表的函数的时候,我们有两种方法来确定到底多少参数会被传递进来。

方法1是在类型固定的参数中指明后面有多少个参数以及他们的类型。printf就是采用的这种方法,它的format参数指明后面每个参数的类型。

方法2是指定一个结束参数。这种情况一般是不定参数拥有同样的类型,我们可以指定一个特定的值来表示参数列表结束。下面这个sum函数就是一个例子:

C语言不定参数的作用,不定参数在C语言中的应用实例

intsumi(intc, ...)

C语言不定参数的作用,不定参数在C语言中的应用实例
C语言不定参数的作用,不定参数在C语言中的应用实例

...{

C语言不定参数的作用,不定参数在C语言中的应用实例

 va_list ap;

C语言不定参数的作用,不定参数在C语言中的应用实例

 va_start(ap,c);

C语言不定参数的作用,不定参数在C语言中的应用实例

inti;

C语言不定参数的作用,不定参数在C语言中的应用实例

intsum=c;

C语言不定参数的作用,不定参数在C语言中的应用实例

 c=va_arg(ap,int);

C语言不定参数的作用,不定参数在C语言中的应用实例

while(0!=c)

C语言不定参数的作用,不定参数在C语言中的应用实例
C语言不定参数的作用,不定参数在C语言中的应用实例

...{

C语言不定参数的作用,不定参数在C语言中的应用实例

 sum=sum+c;

C语言不定参数的作用,不定参数在C语言中的应用实例

 c=va_arg(ap,int);

C语言不定参数的作用,不定参数在C语言中的应用实例

 }

C语言不定参数的作用,不定参数在C语言中的应用实例

returnsum;

C语言不定参数的作用,不定参数在C语言中的应用实例

}

使用这个函数的代码为:

C语言不定参数的作用,不定参数在C语言中的应用实例

intmain(intargc,char*argv[])

C语言不定参数的作用,不定参数在C语言中的应用实例
C语言不定参数的作用,不定参数在C语言中的应用实例

...{

C语言不定参数的作用,不定参数在C语言中的应用实例

inti=sumi(1,2,3,4,5,6,7,8,9,0);

C语言不定参数的作用,不定参数在C语言中的应用实例
C语言不定参数的作用,不定参数在C语言中的应用实例

return0;

C语言不定参数的作用,不定参数在C语言中的应用实例

}

C语言不定参数的作用,不定参数在C语言中的应用实例

访问各个参数

其实前文已经告诉我们怎么去访问不定参数。va_start和va_arg函数可以被结合起来用于依次访问每个函数,他们实际上都是宏函数。

在vc6,va_start函数定义为:

C语言不定参数的作用,不定参数在C语言中的应用实例

#define_INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

C语言不定参数的作用,不定参数在C语言中的应用实例

#defineva_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

其中_INTSIZEOF(n)计算比n大的sizeof(int)的最”妒绻鹡=101,则_INTSIZEOF(n)为104。

va_start执行完毕后,ap指向变量v后第一个4字节对齐的地址。例如,v的地址为0x123456,

v的大小为13,则v后面的下一个与字边界对齐的地址为0x123456+0x0D=0x123463再调整为与4字节对齐的下一个地址,也就是

0x123464.

va_arg函数定义为:

C语言不定参数的作用,不定参数在C语言中的应用实例

#defineva_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

分析与va_start一样,它的结果是使ap指向当前变量的下一个变量。

这样,我们只要在开始时使用va_start把不定参数列表赋值给ap,然后依次用va_arg获得不同参数即可。