</pre>printf函数运行机制</p><p><span style="font-size:18px">例如:printf("hello,world\n");</span></p><p><span style="font-size:18px">当前printf函数只有一个参数,可以知道的是printf函数可以有很多不固定数量的参数;但是对于printf函数来说,它执行的是一个输出打印,一个只读的操作,不管参数的多少,只要参数大于1个,该函数都会按照一定的规则进行参数的输出;但是有着一个规定是,printf函数的第一个参数必须是一个字符串地址;所有的打印输出都是按照这样的一个字符串进行格式化输出的;对于当前的hello,world这个程序,调用了这个程序,printf开始将字符串hello,world的首地址进行压栈;在参数全部压栈完成之后,将会一个参数一个参数的弹出;</span></p><p><span style="font-size:18px">int i = 10,j = 20;</span></p><p><span style="font-size:18px">printf("The i:%d,j:%d\n",i,j);</span></p><p><span style="font-size:18px">从printf函数的参数进行讨论;该函数有三个参数,从j开始进行压栈,然后是i,最后是字符串首地址;当函数压栈完成之后,printf函数将会从栈中取出数据,首先弹出的是字符串首地址,"The i:%d,j:%d\n",当遇到了格式占位符时,将会自动从栈中依次取出数据,进行格式替换;</span></p><p><span style="font-size:18px"></span><pre name="code" class="cpp">int printf(const char * __restrict format, ...)
{
va_list arg;
int rv;
va_start(arg, format);
rv = vfprintf(stdout, format, arg);
va_end(arg);
return rv;
}
Printf函数使用了变参结构(va_list),可以参考其他资料关于讲解va_list;va_start中fomart用来表示第一个参数的首地址用来标示参数参数地址起始地址,使用将之后的参数存到va_list结构体中;可以用va_arg(arg, int)接口来获取参数值,获取完成之后,arg自动指向下一个参数类型;通过扫描格式化串format,遇到%d,%s将对应的值给打印出来;
示例:
void foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
{
if((*fmt++) != '%')
{
continue;
}
switch (*fmt)
{
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
}
va_end(ap);
}