研究printf的实现,,首先来看看printf函数的函数体 int printf(const char *fmt, …){int i;char buf[256]; va_list arg = (va_list)((char*)(&fmt) + 4); i = vsprintf(buf, fmt, arg); write(buf, i); return i; } 代码位置:D:/~/funny/kernel/printf.c 在形参列表里有这么一个token:… 这个是可变形参的一种写法。 当传递参数的个数不确定时,就可以用这种方式来表示。 很显然,我们需要一种方法,来让函数体可以知道具体调用时参数的个数。 先来看printf函数的内容: 这句: va_list arg = (va_list)((char*)(&fmt) + 4); va_list的定义: typedef char *va_list 这说明它是一个字符指针。 其中的: (char*)(&fmt) + 4) 表示的是…中的第一个参数。 如果不懂,我再慢慢的解释: C语言中,参数压栈的方向是从右往左。 也就是说,当调用printf函数的适合,先是最右边的参数入栈。 fmt是一个指针,这个指针指向第一个const参数(const char *fmt)中的第一个元素。 fmt也是个变量,它的位置,是在栈上分配的,它也有地址。 对于一个char *类型的变量,它入栈的是指针,而不是这个char *型变量。 换句话说: 你sizeof(p) (p是一个指针,假设p=&i,i为任何类型的变量都可以) 得到的都是一个固定的值。(我的计算机中都是得到的4) 当然,我还要补充的一点是:栈是从高地址向低地址方向增长的。 ok! 现在我想你该明白了:为什么说(char*)(&fmt) + 4) 表示的是…中的第一个参数的地址。 下面我们来看看下一句: i = vsprintf(buf, fmt, arg); 让我们来看看vsprintf(buf, fmt, arg)是什么函数。
int vsprintf(char *buf, const char *fmt, va_list args) { char* p; char tmp[256]; va_list p_next_arg = args; for (p=buf;*fmt;fmt++) { if (*fmt != ‘%’) { *p++ = *fmt; continue; } fmt++; switch (*fmt) { case ‘x’: itoa(tmp, *((int*)p_next_arg)); strcpy(p, tmp); p_next_arg += 4; p += strlen(tmp); break; case ‘s’: break; default: break; } } return (p – buf); } 伟人所达到并保持着的高处,并不是一飞就到的,