c語言 神奇的可變參數

關於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所佔空間的不同,由於讀取順序的錯誤必然出錯。還是不能想當然啊。

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