先看代碼
printf(“hello,world!”);其參數個數為1個。 printf(“a=%d,b=%s,c=%c”,a,b,c);其參數個數為4個。
如何編寫可變參數函數呢?我們首先來看看printf函數原型是如何定義的。
在linux下,輸入man 3 printf,可以看到prinf函數原型如下:
SYNOPSIS #include <stdio.h> int printf(const char *format, ...);
后面的三個點...表示printf參數個數是不定的.
如何實現可變參數函數?
2. 編寫可變函數准備
為了編寫可變參數函數,我們通常需要用到<stdarg.h>頭文件下定義的以下函數:
void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
其中:
va_list是用於存放參數列表的數據結構。
va_start函數根據初始化last來初始化參數列表。
va_arg函數用於從參數列表中取出一個參數,參數類型由type指定。
va_copy函數用於復制參數列表。
va_end函數執行清理參數列表的工作。
上述函數通常用宏來實現,例如標准ANSI形式下,這些宏的定義是:
typedef char * va_list; //字符串指針 #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 )
使用宏_INTSIZEOF是為了按照整數字節對齊指針,因為c調用協議下面,參數入棧都是整數字節(指針或者值)。
GNU給出的一個實例:
#include <stdio.h> #include <stdarg.h> void foo(char *fmt, ...) { va_list ap; int d; char c, *s; va_start(ap, fmt); while (*fmt) switch (*fmt++) { case 's': /* string */ s = va_arg(ap, char *); printf("string %s\n", s); break; case 'd': /* int */ d = va_arg(ap, int); printf("int %d\n", d); break; case 'c': /* char */ /* need a cast here since va_arg only takes fully promoted types */ c = (char) va_arg(ap, int); printf("char %c\n", c); break; } va_end(ap); }
說明:
va_start(ap, fmt);用於根據fmt初始化可變參數列表。
va_arg(ap, char *);用於從參數列表中取出一個參數,其中的char *用於指定所取的參數的類型為字符串。每次調用va_arg后,參數列表ap都會被更改,以使得下次調用時能得到下一個參數。
va_end(ap);用於對參數列表進行一些清理工作。調用完va_end后,ap便不再有效。
以上程序給了我們一個實現printf函數的是思路,即:通過調用va_start函數,來得到參數列表,然后我們一個個取出參數來進行輸出即可。
3.實例
例如:對於printf(“a=%d,b=%s,c=%c”,a,b,c)語句;fmt的值為a=%d,b=%s,c=%c,調用va_start函數將參數a,b,c存入了ap中。注意到:fmt中的%為特殊字符,緊跟%后的參數指明了參數類型.
因此我們的簡易printf函數如下:
#include <stdio.h> #include <stdarg.h> void myprintf(char *fmt, ...) { va_list ap; int d; double f; char c; char *s; char flag; va_start(ap,fmt); while (*fmt){ flag=*fmt++; if(flag!='%'){ putchar(flag); continue; } flag=*fmt++;//記得后移一位 switch (flag) { case 's': s=va_arg(ap,char*); printf("%s",s); break; case 'd': /* int */ d = va_arg(ap, int); printf("%d", d); break; case 'f': /* double*/ d = va_arg(ap,double); printf("%d", d); break; case 'c': /* char*/ c = (char)va_arg(ap,int); printf("%c", c); break; default: putchar(flag); break; } } va_end(ap); } int main(){ char str[10]="linuxcode"; int i=1024; double f=3.1415926; char c='V'; myprintf("string is:%s,int is:%d,double is:%f,char is :%c",str,i,f,c); }
從上面我們可以知道可變參數函數的編寫,必須要傳入一個參數fmt,用來告訴我們的函數怎樣去確定參數的個數。我們的可變參數函數是通過自己解析這個參數來確定函數參數個數的。
比如,我們編寫一個求和函數,其函數實現如下:
int sum(int cnt,...){ int sum=0; int i; va_list ap; va_start(ap,cnt); for(i=0;i<cnt;++i) sum+=va_arg(ap,int); va_end(ap); return sum; }
原文博客:http://www.jb51.net/article/43192.htm
va_list使用vprintf()實例:
#include <stdio.h> #include <stdarg.h> void my_print(const char* format, ...) { int n; va_list arg_list; va_start(arg_list, format); n = vprintf(format, arg_list); va_end(arg_list); } int main(int argc, char *argv[]) { int ai = 0; my_print("%d\n", ai); return 0; }