本文转载自http://blog.sina.com.cn/s/blog_56e8acf40100kaer.html
printf的声明
_CDEDL调用约定:
对于x86而言,栈向下生长,函数参数从右向左入栈,因此从第一个固定参数(format)地址向前(向上)移动就可得到其他变参的地址。
va_list相关宏(VC++中stdarg.h里x86平台的宏定义)
typedef char *
//_INTSIZEOF(n)宏:将sizeof(n)按sizeof(int)对齐。
#define _INTSIZEOF(n)
//取format参数之后的第一个变参地址,4字节对齐
#define va_start(va_list
//对type类型数据,先取到其四字节对齐地址,再取其值
#define va_arg(va_list
#define va_end(va_list
如何得到参数个数?
其实printf并不知道参数个数,它只是逐个解析format字符串。对于特定类型%,使用va_arg去取相应参数的值,直到遍历字符串结束。类似于如下代码:
如下调用会打印什么内容?
printf("%x");
----------------
背景知识:
函数调用栈帧结构:
比如Z调用A,Z的栈帧:
1.自右向左(约定)压入参数
2.Z中返回地址。即从A返回后Z中下一条指令地址
3.调用者的EBP。由编译器插入指令实现:
"pushl %ebp"
"movl %esp, %ebp"//esp为栈指针
因而形成一个链表。依此可得到调用者的栈顶位置(对于A的EBP,得到Z的EBP地址为0x000f)。
4.局部变量。