printf的实现原理

本文转载自http://blog.sina.com.cn/s/blog_56e8acf40100kaer.html

 

printf的声明
int _cdecl printf(const char* format, …);
_cdecl是C和C++程序的缺省调用方式

_CDEDL调用约定:
1.参数从右到左依次入栈
2.调用者负责清理堆栈
3.参数的数量类型不会导致编译阶段的错误


对于x86而言,栈向下生长,函数参数从右向左入栈,因此从第一个固定参数(format)地址向前(向上)移动就可得到其他变参的地址。

va_list相关宏(VC++中stdarg.h里x86平台的宏定义)

typedef char * va_list;
//_INTSIZEOF(n)宏:将sizeof(n)按sizeof(int)对齐。
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

//取format参数之后的第一个变参地址,4字节对齐
#define va_start(va_list ap, format) ( ap = (va_list)&format+ _INTSIZEOF(format) )

//对type类型数据,先取到其四字节对齐地址,再取其值
#define va_arg(va_list ap,type)
  ( *(type*)((ap += _INTSIZEOF(type)) -_INTSIZEOF(type)) )

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


如何得到参数个数?
其实printf并不知道参数个数,它只是逐个解析format字符串。对于特定类型%,使用va_arg去取相应参数的值,直到遍历字符串结束。类似于如下代码:
#include <stdio.h>
#include <stdarg.h>
void myprintf(const char *format, ...)
{
    va_list ap;
    char ch;
      va_start(ap, format);
      while(ch = *format++)
    {
    switch(c)
    {
        case 'c':
    {
    char ch1 = va_arg(ap, char);
      putchar(ch1);
    break;
    }
     


如下调用会打印什么内容?
printf("%x");


----------------

背景知识:

函数调用栈帧结构:

printf的实现原理

比如Z调用A,Z的栈帧:
1.自右向左(约定)压入参数
2.Z中返回地址。即从A返回后Z中下一条指令地址
3.调用者的EBP。由编译器插入指令实现:
"pushl %ebp"
"movl %esp, %ebp"//esp为栈指针
因而形成一个链表。依此可得到调用者的栈顶位置(对于A的EBP,得到Z的EBP地址为0x000f)。
4.局部变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值