不定參數講座
聲明:
以下的說明與例子由程式火鍋寢室聯誼社的版主《北風》所提供。
個人覺得程式的說明條理分明,十分清楚,在此引用。十分感謝他熱心的幫助。
他所使用的編譯器為Turbo C/C++

在第一個例子裡,不定參數的型態和第一個已確定的int型態相同,

code:

#include <stdarg.h>
int average(int first, ...)
{   
	int count = 0, sum = 0, i = first;   
	va_list marker;   

	va_start(marker, first); /* Initialize variable arguments. */   
	do{      
		sum += i;      
		count++;   
	} while ((i = va_arg(marker, int)) != -1);   
	
	va_end(marker); /* Reset variable arguments. */   
	return (sum ? (sum / count) : 0);
}
void main()
{   
	clrscr();   
	printf("average=%d\n", average(10,20,39,-1));
}

在第二個例子裡,不定參數的型態和第一個已確定的char*t型態不同。
code:

#include <stdarg.h>
int average(char *first, ...)
{   
	int count = 0, sum = 0, i;   
	va_list marker;   

	va_start(marker, first); /* Initialize variable arguments. */   
	while ((i = va_arg(marker, int)) != -1)   
	{      
		sum += i;      
		count++;   
	}   va_end(marker); /* Reset variable arguments. */   
	return (sum ? (sum / count) : 0);
}
void main()
{   
	clrscr();   
	printf("average=%d\n", average("avg",10,20,39,-1));
} 

在第三個例子裡,不定參數的型態有兩種:
average("avg","DDD","aaa",10,20,39,-1));
我們希望第二個和第三個是char*型態,第四個以後轉變成int型態。
那麼如何取得它們的值呢?
code:

#include <stdarg.h>
int average(char *first, ...)
{   
	int count = 0, sum = 0, i;   
	char *buffer;   
	va_list marker;   
	
	va_start(marker, first); /* Initialize variable arguments. */   
	for(i=0; i<2; i++)  /* 將指標連續往下移動兩個變數所在的位址 */   
	{      
		buffer = va_arg(marker, char *);      
		printf("[%s]\n",buffer);   
	}   
	while ((i = va_arg(marker, int)) != -1)   
	{      
		sum += i;      
		count++;   
	}   
	va_end(marker); /* Reset variable arguments. */   
	return (sum ? (sum / count) : 0);
}
void main()
{   
	clrscr();   
	printf("average=%d\n", average("avg","DDD","aaa",10,20,39,-1));
}

在第四個例子裡,有兩種寫法:
average("avg", "DDD", 123L, 23.0, "10", "20", "39", NULL);
雖然第五個參數起才是不定參數的開始,
但是為了徹底了解不定參數的用法,
首先故意將起始的指標放在第一個參數上,
於是你必須抓出第二、三、四參數的值,
將指標逐一的移到第四個參數的尾端,
繼續精確的抓取第五個以後不定參數的位置,
這不是自找麻煩,而是本篇最重要的演練,
唯有透過魔鬼找碴訓練,才能真正驗出自己是否能夠完全設計不定參數,
否則只是一知半解,那麼所設計的不定參數就成了毒死自己的毒藥,
根本不能拿到實戰的沙場上作戰,這是本篇最後的精神重點所在。
code:

int average(char *first, char *second, long ii, double mm, ...)
{   
	int count = 0, sum = 0, i;   
	char *buffer;   
	va_list marker;   

	va_start(marker, first);   
	printf("[%s]\n",va_arg(marker, char *));   
	printf("[%ld]\n",va_arg(marker, long));   
	printf("[%.lf]\n",va_arg(marker, double));   
	while (strcmp(buffer = va_arg(marker, char *), NULL))      
		printf("[%s]\n", buffer);   

	va_end(marker); /* Reset variable arguments. */   
	return (sum ? (sum / count) : 0);
}
void main()
{   
	clrscr();   
	printf("average=%d\n", average("avg", "DDD", 123L, 23.0, "10", "20", "39", NULL));
}

當我們了解上述的寫法用意之後,回歸正式的精簡寫法:

code:

int average(char *first, char *second, long ii, double mm, ...)
{   
	int count = 0, sum = 0, i;   
	char *buffer;   
	va_list marker;   

	va_start(marker, mm);   
	while (strcmp(buffer = va_arg(marker, char *), NULL))      
		printf("[%s]\n", buffer);   
	va_end(marker); /* Reset variable arguments. */   
	return (sum ? (sum / count) : 0);
}
void main()
{   
	clrscr();   
	printf("average=%d\n", average("avg", "DDD", 123L, 23.0, "10", "20", "39", NULL));
}


在以上程式中,有一行程式:
	while (strcmp(buffer = va_arg(marker, char *), NULL))   
這一行程式要特別說明。
strcmp(str1,str2)會存取str1str2內容
在Real mode時,存取記憶體,沒有做任何的保護或檢查,就算存取到系統或不合法的
記憶體位址,系統不會產生任何的所謂「Access Exception」
而在Protect mode時,存取記憶時,會針對記憶體做保護及檢查的工作
確保被存取的記憶體是合法,若此記憶體未經授權或不合法時
系統會產生Access Exception來告知使用者。
	strcmp(str1,NULL)

strcmp會存取str1NULL pointer的內容,一般定義NULL0
存取記憶體位址0,是不合法的行為,在Read mode中,並不會檢查出來
而在Protect mode中,會產生Access Exception來告知使用者。
若你試著將例子中包含以上程式碼,放在Visual C++或及它在Win上跑的編譯器
你會得到Access Exception。比較正確的寫法應該是:
	while (buffer = va_arg(marker, char *)
在此提供補充說明。
目錄

Written By James On 2004/09/06
Hosted by www.Geocities.ws

1