Chinaunix首页 | 论坛 | 博客
  • 博客访问: 213125
  • 博文数量: 37
  • 博客积分: 2649
  • 博客等级: 少校
  • 技术积分: 490
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-02 10:31
文章分类

全部博文(37)

文章存档

2012年(3)

2009年(7)

2008年(27)

我的朋友

分类: C/C++

2008-12-16 09:29:04

    C语言中有些函数使用可变参数,比如常见的int printf( const char* format, ...
),第一个参数format是固定的,其余的参数的个数和类型都不固定。

C语言用va_start等宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,
就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参
数的地址。下面我们来分析这些宏。

在stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:


Quote:

typedef char * va_list;

  #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1
) )

  #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

  #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )


  #define va_end(ap) ( ap = (va_list)0 )


_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeo
f(int)对齐。一般的sizeof(int)= 4,也就是参数在内存中的地址都为4的倍数。比如,如
果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在 5-8之间,那么_IN
TSIZEOF(n)=8。具体细节可以参见:数据在机器内的存储与运算章节。

在这些宏中,va就是variable argument(可变参数)的意思;arg_ptr是指向可变参数表的
指针;prev_param则指可变参数表的前一个固定参数;type为可变参数的类型。va_list也
是一个宏,其定义为typedef char * va_list,实质上是一char型指针。char型指针的特
点是++、--操作对其作用的结果是增1和减1(因为sizeof(char)为1),与之不同的是int
等其它类型指针的++、--操作对其作用的结果是增sizeof(type)或减sizeof(type),而且
sizeof (type)大于1。

结论:
(1)通过va_start宏我们可以取得可变参数表的首指针;显而易见,其含义为将最后那个
固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。

(2)va_arg宏的意思则指取出当前arg_ptr所指的可变参数并将ap指针指向下一可变参数

(3)va_end宏被用来结束可变参数的获取;可以看出,va_end ( list )实际上被定义为
空,没有任何真实对应的代码,用于代码对称,与va_start对应;另外,它还可能发挥代
码的"自注释"作用。所谓代码的"自注释",指的是代码能自己注释自己。

为了能从固定参数依次得到每个可变参数,va_start,va_arg充分利用下面两点:

  1. C语言在函数调用时,先将最后一个参数压入栈

  2. X86平台下的内存分配顺序是从高地址内存到低地址内存

  高位地址

  第N个可变参数

  。。。

  第二个可变参数

  第一个可变参数 ? ap

  固定参数 ? v
阅读(1221) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~