有這樣一個簡單的問題:求從1加到100的和S(S= 1+2+3+...+99+100),並打印出S。這道題應該是我們剛剛開始學編程語言時老師讓我們做的題目吧一個for或者while循環S就搞定了,接下來就是輸出S ,我相信對於當時的你百分之百會用printf(),即使現在遇到類似的問題,在大多情況下你也會用printf的。你有沒有想過,我們爲什麼就要用它呢?printf函數是怎樣寫的?你是否仔細的想過:爲什麼我們想讓它輸出什麼它就能輸出什麼?現在,我們就帶着這些問題一起來探討一下神奇的printf().
假如我想打印一串字符:Hello World!(記得當時我們C語言教材上第一個例子就是它),你的解決辦法是什麼?我想最簡單最直接的辦法就是用printf,如{printf("Hello World!");} 。在舉個例子,還是求S= 1+2+3+...+99+100並打印出結果,求的和S=5050,如何打印呢?還是用printf。怎麼,printf想打印什麼數據類型就打印什麼數據類型嗎?這時,我們一起查一下手冊,我們發現printf的輸出功能是如此的強大,各種數據類型它是通吃啊!不僅如此,它還能將多個數據類型的組合一同打印出來。這時,我們的問題就來了,它到底是怎樣實現數據類型的識別的?特別是printf函數是怎樣實現自由變參的?
對於printf如何實現數據類型的識別,這點很容易:當遇到%的時候就對下一個字符做判斷,如果是d,那麼輸出的應該是int型的數;如果是s,那麼輸出的應該是字符串;如果是f,那麼輸出的應該是float型的數等等。其實這些都很容易理解並且實現。在我看來,printf的精髓所在就是它變參的實現。下面就着重說一下變參的實現過程。
其實printf的變參看起來挺奇妙,不過當你實際瞭解它的實現之後,或許你可能會想:也就那樣了!或許你可能會感嘆:原來如此!不管怎樣,我們先一睹它的真容吧!
可變參數主要由stdarg.h中定義的四個宏實現,具體步驟如下:
step 1:在調用參數表之前,應該定義一個 va_list 類型的變量,以供後用(下面假設這個 va_list 類型變量被定義爲ap)
step 2:然後對該ap進行初始化,讓它指向可變參數表裏面的第一個參數,這是通過 va_start 來實現的,第一個參數是 ap 本身,第二個參數是在變參表前面緊挨着的一個變量
step 3:然後是獲取參數,調用 va_arg,它的第一個參數是 ap,第二個參數是要獲取的參數的指定類型,然後返回這個指定類型的值,並且把 ap 的位置指向變參表的下一個變量位置
step 4:獲取所有的參數之後,我們有必要將這個 ap 指針關掉,以免發生危險,方法是調用 va_end,他是輸入的參數 ap 置爲 NULL,應該養成獲取完參數表之後關閉指針的習慣
看到了吧,實現的過程就是這麼簡單,你可以自己親自動手寫一個試試。
上面就是實現printf的主要部分,有關某些細節的東西我自己也想不了那麼多。我對printf的理解也就是這麼個樣子了。
用了這麼長時間的printf了,也曾經想過爲什麼,但就是沒有自己實現它。現在趁着國慶的假期,在家裏除了吃喝,剩下的就是大把的時間了。於是乎,我便下決心珍惜這短暫的光陰,自己動手寫printf函數。我很享受寫它的過程,畢竟對它很瞭解了。下面就是我自己完成的printf------>printf_FC.
代碼如下:
頭文件
#include <stdio.h>
#include <stdarg.h>
/***The printf_FC equals to printf which incuds in <stdio.h> ***/
int printf_FC(const char *format, ...)
{
va_list unnamed_p;
char *p, *sval;
unsigned int value_i;
va_start( unnamed_p, format);
for ( p=(char *)format; *p; p++ )
{
if ( *p != '%' )
{
putchar( *p );
continue;
}
switch ( *++p )
{
case 'd':
value_i = va_arg( unnamed_p, unsigned int );
output_convert( value_i, 10 );
break;
case 's':
for (sval = va_arg(unnamed_p, char*); *sval; sval++)
putchar( *sval );
break;
case 'x':
value_i = va_arg( unnamed_p, unsigned int );
output_convert( value_i, 16 );
break;
default:
putchar( *p );
break;
}
}
va_end( unnamed_p );
return 0;
}
/***The function is to convert an integer to int or hex***/
void output_convert( unsigned int num, const int base )
{
const char *digit = "0123456789ABCDEF";
unsigned int buf[32];
int i = 0;
char ch;
if (base == 10)
{
do
{
buf[i] = num % 10;
num = num / 10;
i++;
} while ( num > 0 );
}
if (base == 16)
{
do
{
buf[i] = num % 16;
num = num / 16;
i++;
} while ( num > 0 );
}
while ( --i >= 0 )
{
ch = digit[buf[i]];
putchar( ch );
}
}
/*** main() and test function ***/
int printf_test(void)
{
char buf[16] = "abcd";
int counter = 0;
printf_FC("hello, test printf! \n");
printf_FC("test str: %s \n", buf);
for(; counter < 5 ; counter++)
{
printf_FC("test int: %d \n", counter);
printf_FC("test hex: 0x%x \n", counter);
printf_FC("\n");
}
return 0;
}
int main()
{
printf_test();
return 0;
}
Testing Result As Follows: