C解析之十二C语言prinft函数的秘密

[复制链接]
1960|8
 楼主| elecintop 发表于 2014-4-29 10:18 | 显示全部楼层 |阅读模式
 楼主| elecintop 发表于 2014-4-29 10:19 | 显示全部楼层
从C语言的第一个程序Hello World开始,到目前所写的几乎所有C程序都有它的出场,它便是: 标准化输出函数printf。printf超高的出场率让人对它习以为常,似乎printf函数的独特性也不被人所注意。
 楼主| elecintop 发表于 2014-4-29 10:20 | 显示全部楼层
1.变长参数:不可思议
你可能没有注意:printf函数的参数是变长参数。
printf("Hello World !");
ptintf("%d",number);
printf("Two number:%d %d",number1,number2);
首先,试图从函数相关的知识解析这个问题,...  ,最后只能得出一个结论:不可思议。
 楼主| elecintop 发表于 2014-4-29 10:21 | 显示全部楼层
2.printf常见使用格式:
printf(char *format,arg1,arg2 ...);
其中format称为格式化字符串:包含普通字符和转换字符。输出时,普通字符原封不动地复制到输出流中,转换字符则用于控制参数的输出方式。
arg1,arg2  ...是参数列表。
如:printf("Hello World !");
格式化字符串仅包含普通字符串"Hello World !",则"Hello World !"按原样输出。
再如: printf("Two number:%d %d",number1,number2);
格式化字符串包含普通字符串"Two number:"和转换字符串"%d %d","Two number:"原样输出,转换字符串则用于控制number1,number2的输出。
 楼主| elecintop 发表于 2014-4-29 10:22 | 显示全部楼层
3.prinft定义格式:
int printf(char *format, ... )
此处的省略号...表示参数的数量与类型可变。
关键:如何处理...代表的参数表,它甚至连名字都没有。答案在标准头文件<stdarg.h>中的一组宏定义,这组宏定义提供了遍历参数表的方法。va_list类型用于声明一个变量,该变量将依次引用参数表的各个参数。va_start将va_list变量指向第一个无名参数。va_arg返回参数表中的一个参数,并将va_list变量指向下一个无名参数。va_arg根据一个数据类型名决定返回参数的类型与指针移到的步长。va_end用于最后做一些清理工作。
 楼主| elecintop 发表于 2014-4-29 10:23 | 显示全部楼层
4.编写自己的printf函数:
  1. #include<stdarg.h> //宏定义在该头文件内
  2. #include<stdio.h> //需要putchar,printf支持(某些类型转化),
  3. void Myprintf(char * format,...)
  4. {
  5. va_list ap; //定义一个va_list变量,用来遍历无名参数表
  6. char *p; //p用于变量 格式化字符串format
  7. char *sval; //存储提取的字符串参数
  8. int ival; //存储提取的整型参数
  9. double dval; //存储提取的浮点型参数
  10. va_start(ap,format); //va_start以最后一个有名参数format为参数,将ap指向第一个无名参数
  11. for(p=format;*p;p++)
  12. {
  13. if(*p!='%')
  14. {
  15. putchar(*p);
  16. continue;
  17. }
  18. switch(*++p)
  19. {
  20. case 'd':
  21. ival=va_arg(ap,int); //va_arg根据int,返回一个int参数,并决定指针偏移的步长(int显然为4)
  22. printf("%d",ival);
  23. break;
  24. case 'f':
  25. dval=va_arg(ap,double); //va_arg返回一个double,指针偏移8
  26. printf("%f",dval);
  27. break;
  28. case 's':
  29. for(sval=va_arg(ap,char *);*sval;sval++)
  30. putchar(*sval);
  31. break;
  32. default:
  33. putchar(*p);
  34. break;
  35. }
  36. }
  37. va_end(ap); //结束时清理工作
  38. }
  39. int main(){
  40. int n=10;
  41. double m=8.8;
  42. char s[10]="hello";
  43. Myprintf("This is test !\n");
  44. Myprintf("This int : %d \n",n);
  45. Myprintf("This double:%f\n",m);
  46. Myprintf("This string:%s\n",s);
  47. return 0;
  48. }
 楼主| elecintop 发表于 2014-4-29 10:23 | 显示全部楼层
实现一个Myprintf很容易吧,出于复杂度与突出重点的考虑,Myprintf没有实现自己的int,double转换,它的int,double的转换实际上借用了printf的转换,所以需要包含stdio.h,但不要纠结于这些无关紧要的细节。通过Myprintf,printf如何使用变长的参数表,printf的工作机制便清晰地浮出水面。
一毛钱0 发表于 2014-5-8 12:09 | 显示全部楼层
xy123151 发表于 2014-5-10 17:10 | 显示全部楼层
长姿势了,平常用着printf都没想过是什么原理
您需要登录后才可以回帖 登录 | 注册

本版积分规则

176

主题

1329

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部