Bru*_*uce 23 c gcc variadic-functions calling-convention
int max(int n, ...)
我正在使用cdecl调用约定,其中调用者在被调用者返回后清理变量.
我想知道怎么做宏va_end,va_start和va_arg工作?
调用者是否将参数数组的地址作为max的第二个参数传递?
Ski*_*izz 28
如果你看看C语言将参数存储在堆栈中的方式,宏的工作方式应该变得清晰: -
Higher memory address    Last parameter
                         Penultimate parameter
                         ....
                         Second parameter
Lower memory address     First parameter
       StackPointer  ->  Return address
(注意,根据硬件的不同,堆栈指针可能会向下一行,而较高和较低的指针可能会被交换)
即使没有参数类型,参数总是像这样存储1....
该va_start宏只设置了一个指向第一个功能参数,例如: -
 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;
 }
这p指向第二个参数.该va_arg宏做到这一点: -
 void func (int a, ...)
 { 
   // va_start
   char *p = (char *) &a + sizeof a;
   // va_arg
   int i1 = *((int *)p);
   p += sizeof (int);
   // va_arg
   int i2 = *((int *)p);
   p += sizeof (int);
   // va_arg
   long i2 = *((long *)p);
   p += sizeof (long);
 }
该va_end宏仅设置p值NULL.
笔记:
...参数的存在将关闭此功能并使编译器使用堆栈.当参数在堆栈上传递时,va_"函数"(它们大部分时间都是作为宏实现的)只是简单地操作私有堆栈指针.此私有堆栈指针从传递给的参数中存储va_start,然后va_arg在迭代参数时从"堆栈""弹出"参数.
假设您max使用三个参数调用该函数,如下所示:
max(a, b, c);
在max函数内部,堆栈基本上如下所示:
      +-----+
      |  c  |
      |  b  |
      |  a  |
      | ret |
SP -> +-----+
SP是真正的堆栈指针,它不是真的a,b而且c在堆栈上但它们的值.ret是返回地址,在函数完成时跳转到的位置.
什么va_start(ap, n)是参数的地址(n在你的函数原型中)并从中计算下一个参数的位置,所以我们得到一个新的私有堆栈指针:
      +-----+
      |  c  |
ap -> |  b  |
      |  a  |
      | ret |
SP -> +-----+
当你使用va_arg(ap, int)它时,返回私有堆栈指针所指向的内容,然后通过将私有堆栈指针更改为现在指向下一个参数来"弹出"它.堆栈现在看起来像这样:
      +-----+
ap -> |  c  |
      |  b  |
      |  a  |
      | ret |
SP -> +-----+
该描述当然是简化的,但显示了原理.
| 归档时间: | 
 | 
| 查看次数: | 13546 次 | 
| 最近记录: |