printf可变参数的实现

来源:互联网 发布:mac录屏如何录制声音 编辑:程序博客网 时间:2024/05/18 08:35

我是大自然的搬运工。。。。。


print.h

[cpp] view plaincopy
  1. #ifndef     __PRINT_H_  
  2. #define    __PRINT_H_  
  3.   
  4. void    print(char* fmt, ...);  
  5. void    printch(char ch);  
  6. void    printdec(int dec);  
  7. void    printflt(double flt);  
  8. void    printbin(int bin);  
  9. void    printhex(int hex);  
  10. void    printstr(char* str);  
  11.   
  12. #define console_print(ch)    putchar(ch)  
  13.   
  14. #endif    /*#ifndef __PRINT_H_*/  

上面print函数为全功能的打印函数,可以实现类似printf的功能,printch实现单个字符的打印、printdec实现十进制格式数字的打印,printflt实现浮点数的打印,printbin实现二进制格式数字的打印,printhex实现十六进制格式数字的打印,printstr实现字符串的打印,console_print函数是串口单字符打印函数的宏定义,这里暂时用PC终端单字符打印函数putchar代替。在实际嵌入式环境下,替换成串口单字符打印函数即可。
print.c

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdarg.h>  
  3. #include "print.h"  
  4.   
  5. int main(void)  
  6. {  
  7.     print("print: %c\n"'c');  
  8.     print("print %d\n", 1234567);  
  9.     print("print: %f\n", 1234567.1234567);  
  10.     print("print: %s\n""string test");  
  11.     print("print: %b\n", 0x12345ff);  
  12.     print("print: %x\n", 0xabcdef);  
  13.     print("print: %%\n");  
  14.     return 0;  
  15. }  
  16.   
  17. void    print(char* fmt, ...)  
  18. {  
  19.     double vargflt = 0;  
  20.     int  vargint = 0;  
  21.     char* vargpch = NULL;  
  22.     char vargch = 0;  
  23.     char* pfmt = NULL;  
  24.     va_list vp;  
  25.   
  26.     va_start(vp, fmt);  
  27.     pfmt = fmt;  
  28.   
  29.     while(*pfmt)  
  30.     {  
  31.         if(*pfmt == '%')  
  32.         {  
  33.             switch(*(++pfmt))  
  34.             {  
  35.                   
  36.                 case 'c':  
  37.                     vargch = va_arg(vp, int);   
  38.                     /*    va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI 
  39.                         mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */  
  40.                     printch(vargch);  
  41.                     break;  
  42.                 case 'd':  
  43.                 case 'i':  
  44.                     vargint = va_arg(vp, int);  
  45.                     printdec(vargint);  
  46.                     break;  
  47.                 case 'f':  
  48.                     vargflt = va_arg(vp, double);  
  49.                     /*    va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI 
  50.                         mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */  
  51.                     printflt(vargflt);  
  52.                     break;  
  53.                 case 's':  
  54.                     vargpch = va_arg(vp, char*);  
  55.                     printstr(vargpch);  
  56.                     break;  
  57.                 case 'b':  
  58.                 case 'B':  
  59.                     vargint = va_arg(vp, int);  
  60.                     printbin(vargint);  
  61.                     break;  
  62.                 case 'x':  
  63.                 case 'X':  
  64.                     vargint = va_arg(vp, int);  
  65.                     printhex(vargint);  
  66.                     break;  
  67.                 case '%':  
  68.                     printch('%');  
  69.                     break;  
  70.                 default:  
  71.                     break;  
  72.             }  
  73.             pfmt++;  
  74.         }  
  75.         else  
  76.         {  
  77.             printch(*pfmt++);  
  78.         }  
  79.     }  
  80.     va_end(vp);  
  81. }  
  82.   
  83. void    printch(char ch)  
  84. {  
  85.     console_print(ch);  
  86. }  
  87.   
  88. void    printdec(int dec)  
  89. {  
  90.     if(dec==0)  
  91.     {  
  92.         return;  
  93.     }  
  94.     printdec(dec/10);  
  95.     printch( (char)(dec%10 + '0'));  
  96. }  
  97.   
  98. void    printflt(double flt)  
  99. {  
  100.     int icnt = 0;  
  101.     int tmpint = 0;  
  102.       
  103.     tmpint = (int)flt;  
  104.     printdec(tmpint);  
  105.     printch('.');  
  106.     flt = flt - tmpint;  
  107.     tmpint = (int)(flt * 1000000);  
  108.     printdec(tmpint);  
  109. }  
  110.   
  111. void    printstr(char* str)  
  112. {  
  113.     while(*str)  
  114.     {  
  115.         printch(*str++);  
  116.     }  
  117. }  
  118.   
  119. void    printbin(int bin)  
  120. {  
  121.     if(bin == 0)  
  122.     {  
  123.         printstr("0b");  
  124.         return;  
  125.     }  
  126.     printbin(bin/2);  
  127.     printch( (char)(bin%2 + '0'));  
  128. }  
  129.   
  130. void    printhex(int hex)  
  131. {  
  132.     if(hex==0)  
  133.     {  
  134.         printstr("0x");  
  135.         return;  
  136.     }  
  137.     printhex(hex/16);  
  138.     if(hex < 10)  
  139.     {  
  140.         printch((char)(hex%16 + '0'));  
  141.     }  
  142.     else  
  143.     {  
  144.         printch((char)(hex%16 - 10 + 'a' ));  
  145.     }  
  146. }  

编译执行结果如下:

[cpp] view plaincopy
  1. print: c  
  2. print: 1234567  
  3. print: 1234567.123456  
  4. print: string test  
  5. print: 0b1001000110100010111111111  
  6. print: 0xabcdef  
  7. print: %  

如上所示,print函数实现了类似printf的功能。

总结:

1、函数参数的传递原理
函数参数是以数据结构:栈的形式存取,从右至左入栈,因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

2、下面是 <stdarg.h> 里面重要的几个宏定义如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type ); 
void va_end ( va_list ap );

实现步骤:
a、va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
      在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);

b、然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
c、接着调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置。

d、 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL。


0 0
原创粉丝点击