首先看printf函数的定义:
1 | static int printf(const char *fmt, ...) |
2 | { |
3 | va_list args; |
4 | int i; |
5 | |
6 | va_start(args, fmt); |
7 | write(1,printbuf,i=vsprintf(printbuf, fmt, args)); |
8 | va_end(args); |
9 | return i; |
10 | } |
参数中采用了可变参数的定义,可变参数的一系列实现函数va函数如下:
va_list arg_ptr;
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针。然后使用va_start使arg_ptr指针指向prev_param的下一位,然后使用va_args取出从arg_ptr开始的type类型长度的数据,并返回这个数据,最后使用va_end结束可变参数的获取。
我们可以看下可变参数调用原理:
C语言中,参数压栈的方向是从右往左。也就是说,当调用printf函数的适合,先是最右边的参数入栈。fmt是一个指针,这个指针指向第一个const参数(const char *fmt)中的第一个元素。
fmt也是个变量,它的位置,是在栈上分配的,它也有地址。
对于一个char *类型的变量,它入栈的是指针,而不是这个char *型变量。
可变参数函数调用原理(其中涉及的数字皆为举例) ========================================================================================= i = 0x23; j = 0x78; char fmt[] = "%x%d"; printf(fmt, i, j); push j push i push fmt call printf add esp, 3 * 4 ┃ HIGH ┃ ┃ HIGH ┃ ┃ ... ┃ ┃ ... ┃ ┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫ ┃ ┃ 0x32010┃ '\0' ┃ ┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫ 0x3046C┃ 0x78 ┃ 0x3200c┃ d ┃ ┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫ arg = 0x30468┃ 0x23 ┃ 0x32008┃ % ┃ ┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫ 0x30464┃ 0x32000 ───╂────┐ 0x32004┃ x ┃ ┣━━━━━━━━━━┫ │ ┣━━━━━━━━━━┫ ┃ ┃ └──→ 0x32000┃ % ┃ ┣━━━━━━━━━━┫ ┣━━━━━━━━━━┫ ┃ ... ┃ ┃ ... ┃ ┃ LOW ┃ ┃ LOW ┃ 实际上,调用 vsprintf 的情形是这样的: vsLprintf(buf, 0x32000, 0x30468); *****************************************************************************************