资源描述:
《printf实现原理》由会员上传分享,免费在线阅读,更多相关内容在教育资源-天天文库。
1、在开发MTK的时候,总习惯一直跟踪代码,一层一层的跳进去看个究竟。看到dbg_print(char*fmt,...)这个函数,看了函数体,发现它实现了我从前一直疑惑的一个问题,Printf的格式化输出是怎么实现的,查了一些关于可变参数函数的资料,并把mtk中printf格式化字符串的实现方式附上,希望对大家有用: 1.要在函数中使用参数,首先要包含头文件。 这个头文件声明了一个va_list类型,定义了四个宏,用来遍历可变参数列表。 voidva_start(va_listap,last); typeva_ar
2、g(va_listap,type); voidva_end(va_listap); voidva_copy(va_listdest,va_listsrc); 下面详细介绍这些宏定义: 2.voidva_start(va_listap,last) va_start必须第一个调用,它初始化va_list类型的变量ap,使ap指向第一个可选参数。参数last是可变参数列表(即函数原型中的省略号…)的前一个参数的名字,也就是最后类型已确定的函数参数名。因为这个参数的地址将会被宏va_start用到,所以最好不要是寄存
3、器变量,函数,或者数组。 对于有可变长参数,但是在可变长参数前没有任何的固定参数的函数,如intfunc(...)是不允许的。这是ANSIC所要求的,变参函数在...之前至少得有一个固定参数。这个参 数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值。 typeva_arg(va_listap,type) 宏va_arg展开后是关于下一个参数的类型和值的表达式,参数type是明确的类型名。 va_arg返回参数列表中的当前参数并使ap指向参数列表
4、中的下一个参数。 voidva_end(va_listap) 每次调用va_start就必须相应的调用va_end销毁变量ap,即将指针ap置为NULL。 voidva_copy(va_listdest,va_listsrc) 复制va_list类型的变量。 每次调用va_copy,也必须有相应的va_end调用。 调用者在实际调用参数个数可变的函数时,要通过一定的方法指明实际参数的个数,考试大提示:例如把最后一个参数置为空字符串(系统调用execl()就是这样的)、-1或其他的方式(函数printf()就
5、是通过第一个参数,即输出格式的定义来确定实际参数的个数的)。 3.举例: #include #include intmain() {inta,b,c,d,e; intmax(int,int...); cin>>a>>b>>c>>d>>e; cout<<"Thebiggerbetweenaandbis"< cout<<"Thebiggerinthefivenumberis"< return0; } intmax(intnum,intinteger...) {va_listap; intm=in
6、teger; va_start(ap,integer); for(inti=1;i {intt=va_arg(ap,int); if(t>m)m=t; cout< } va_end(ap); returnm; } 附:MTK中dbg_print函数的实现: voiddbg_print(char*fmt,...) { va_listap; doubledval; intival; char*p,*sval;char*bp,cval; intfract; unsignedshortlen
7、; charbuffer[1000]; memset(buffer,0,1000); bp=buffer; *bp=0; va_start(ap,fmt); for(p=fmt;*p;p++) { if(*p!="%") { *bp++=*p; continue; } switch(*++p){ case"d": ival=va_arg(ap,int); if(ival<0){ *bp++="-"; ival=-ival; } itoa(&bp,ival,10); break;
8、 case"o": ival=va_arg(ap,int);if(ival<0){ *bp++="-"; ival=-ival; } *bp++="0"; itoa(&bp,ival,8); break; case"x": ival=va_arg(ap,int); if(ival<0){ *bp++="-"