Chinaunix首页 | 论坛 | 博客
  • 博客访问: 141707
  • 博文数量: 25
  • 博客积分: 100
  • 博客等级: 中士
  • 技术积分: 310
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-03 18:56
文章分类

全部博文(25)

文章存档

2012年(4)

2011年(21)

分类: C/C++

2011-06-26 17:39:48

C中的printf是可变参数函数的典型,可变参数函数做出了一些完全脱离编译器的栈访问,通过第一个字符串参数解析来决定如何从栈中获取参数。(这是非常危险的,虽然这个函数对栈区只读不写,不会造车strcopy一类的漏洞,但可能会给调试造成困惑;gcc针对printf等库函数实现了__attribute__(format)保护,如果参数个数和字符串中需要的不符合,编译时就会出现一个warning)。
言归正传,可变参数的实现主要依靠两个宏定义:
(1)va_start 获取第一个可变参数的地址:

  1. #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
  2. #define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )
这里&v是最后一个固定参数(也就是那个转义字符串)的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap,v)以后,ap指向第一个可变参数在的内存地址。_INTSIZEOF(n) 可以理解为一个加强版的sizeof,他可以计算地址对齐后实际占用的空间。

(2)va_arg 取参数值并指向下一个参数:
  1. #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
必须注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量、函数、或者数组类型。

有了这两个宏,用一个循环就可以把栈区里面的参数一个个取出来,再根据格式化类型转换成相应的字符串,最后打印出来。

参考:
阅读(2466) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~