C语言的变参

C语言的变参

函数的变参实际上就是:C语言利用调用栈而提供的一种参数传递的机制。

一、调用栈

      我们知道C语言的调用约定为__cdecl,它的特点为:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。要了解它的确切含义,就先看一下函数调用是发生了什么,假设有如下的程序:

1: #include   
2: #include   
3:    
4: void foo(int count, ...)  
5: {  
6:     va_list args;  
7:     int t = 0;  
8:     int i = 0; 
9:   
10:     va_start(args, i); 
11:   
12:     for (i = 0; i < count; ++i) { 
13:         t = va_arg(args, int); 
14:         printf("%d", t); 
15:     } 
16:  
17:     va_end(args); 
18: } 
19:   
20: int main(int argc, char * argv[])
21: { 
22:     foo(5, 1, 2, 3, 4, 5); 
23:     return 0;
24: } 
25: 
      在这段代码中,定义了一个带变参的函数foo,第一个参数为变参的个数,然后在main函数中对其进行了调用。当我们在调用foo函数的时候,按照调用约定,栈如下图所示:

 

      我们知道,栈是向低地址方向生长的,当调用foo时,先由右向左将参数如栈,最后将返回地址如栈,这个结构只有调用者知道,这是调用约定规定的。

二、被调用函数内部

      进入foo函数以后,foo函数内部是对上图栈的结构一无所知,准确的来说是变参的部分一无所知。它只知道它被调用了,有一个参数为变参的个数。为了获取这些变参,我们可以通过宏va_start来获取参数列表,这个宏有一个参数指定了第一个变参在那个参数后面。通过上图我们可以看出,要获取第一个变参的地址必之它的地址在count参数的后面。

      获取每一个参数是通过宏va_arg来进行的,它需要指定每个参数的类型。从这一点我们可以看出,foo函数真的对栈上的变参情况一无所知。这一点告诉我们,foo函数内部要想主动知道变参的个数,是不可能的。当然,你可以通过其它方式获知。而且,刚好要获取上面的每个参数的首地址,只需要进行这样的计算即可:第一个变参的地址 + sizeof(参数类型)。我都怀疑,参数的入栈顺序是因为方便取才从右边的参数先如栈的。

      最后一个和变参相关的宏是va_end,它实际上是简单的将变参表的指针置为空。

三、总结

      有了上面的知识以后,我们可以确切的回答一些和变参相关的问题了。比如,我以前考虑过的:

      1. 变参可以直接往下一层函数调用传吗?
      显然不行,因为带变参的函数内部对变参一无所知(变参的个数和类型),也就无法直接向下传递了。但是,你可以将变参表的首地址传递下去。

      2. 上面提到的获取变参的个数的问题。

      3. 可以定义变参函数为bar(…)的形式吗?
      显然不行,因为这样的话就无法获取变参表的首地址了。而且,这也是ANSI C 不允许的。

 


本篇文章来源于:开发学院 http://edu.codepub.com   原文链接:http://edu.codepub.com/2010/1223/28251.php

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值