printf可變參數實現

print.h

  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

  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. }  

編譯執行結果如下:

  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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章