關於sprintf,printf,這些函數,參數都是不固定的。而這樣編程方式,也可以自己來搞定。c語言提供了幾個宏就是做這個的。
va_list //一個很特殊的類型
type va_arg(va_list ap, type);
void va_start(va_list ap, last_arg);
void va_end(va_list ap);
void va_copy(va_list dest,va_list src);
這個va_list是一個完整的對象類型,適用於保存宏va_start,va_copy,va_arg和va_end所需的信息。
如果創建了一個va_list實例,傳遞給另一個函數,並通過該函數中的va_arg使用,則在調用函數中的任何後續使用都應該在調用va_end之前進行。
先看一個最簡單的例子
#include <stdarg.h>
int Sum(int n, ...) {
int i;
int sum = 0;
va_list mark;
//va_list sub;
va_start(mark, n);
//va_copy(sub, mark);
printf("n is %d\n", n);
for (i = 0; i < n; ++i) {
int tmp = va_arg(mark, int);
//int tmp = va_arg(sub, int);
sum += tmp;
printf("val is %d\n", tmp);
}
va_end(mark);
//va_end(sub);
return sum;
}
int main() {
int ret = Sum(5, 1, 2, 3, 4, 5);
printf("\nsum is %d", ret);
return 0;
}
可以簡單理解va_start就是初始化,其第二個參數 last_arg在本例中可發現就是“n”,這個就是最後接收到的固定參數。爲什麼是最後接收到,是因爲參數的壓棧順序,這個可自行google。
另外,我把va_copy的註釋起來了,這個就是簡單的拷貝。然後va_end看頭文件就是定義爲((void)(ap = (va_list)0))。也沒什麼好說的。
但是這樣簡單應用看起來也沒啥用。下面的寫法便會讓人一目瞭然。
#include <stdio.h>
#include <stdarg.h>
char buffer[80];
int vspfunc(char* format, ...)
{
va_list aptr;
int ret;
va_start(aptr, format);
ret = vsprintf(buffer, format, aptr);
printf("test1 %d\n", va_arg(aptr, int));
printf("test2 %d\n", va_arg(aptr, int));
printf("test3 %s\n", va_arg(aptr, char*));
va_end(aptr);
return(ret);
}
int main()
{
int i = 5;
int f = 27;
char str[50] = "tutoriasyiibai.com";
vspfunc("%d %d %s", i, f, str);
printf("\n\n%s", buffer);
return(0);
}
這就好像sprintf一樣,使用vsprintf,我把這3部分黏貼到了buffer中。這對於像做日誌系統等的還是有些用處的。
需要注意的是,我在vspfunc裏三個printf,我本來看va_arg菜鳥驛站的解釋(這個宏檢索函數參數列表中類型爲 type 的下一個參數),還以爲順序不重要,對這個例子,因爲只有一個char*,所以怎麼都可以打印出來,而int有兩個,所以那倆int纔有順序。
試了下發現,先打印char*還是會報錯的。還是要按照順序來的。看了下頭文件的定義,發現原來並沒有搞什麼緩存之類的,所以我這個例子,char*和int所佔空間的不同,由於讀取順序的錯誤必然出錯。還是不能想當然啊。