C語言中實現參數個數可變的函數
採用C語言編程的時候,函數中形式參數的數目通常是確定的,在調用時要依次給出與形式參數對應的所有實際參數。但在某些情況下希望函數的參數個數可以根據需要確定。典型的例子有大家熟悉的函數printf()、scanf()和系統調用execl()等。那麼它們是怎樣實現的呢?C編譯器通常提供了一系列處理這種情況的宏,以屏蔽不同的硬體平台造成的差異,增加程式的可移植性。這些宏包括va_start、va_arg和va_end等。
使用這些宏有兩種不同的形式,二者在程式中包括的標頭檔不同,宏的定義也存在一些差別。
採用ANSI標準形式時,參數個數可變的函數的原型聲明是:
type funcname(type para1, type para2, ...)
這種形式至少需要一個普通的形式參數,後面的省略符號不表示省略,而是函數原型的一部分。type是函數傳回值和形式參數的類型。
採用與UNIX System V相容的聲明方式時,參數個數可變的函數原型是:
type funcname(va_alist)
va_dcl
這種形式不需要提供任何普通的形式參數。type是函數傳回值的類型。va_dcl是對函數原型聲明中參數va_alist的詳細聲明,實際是一個宏定義,對不同的硬體平台採用不同的類型來定義,但在最後都包括了一個分號。因此va_dcl後不再需要加上分號了。va_dcl在代碼中必須原樣給出。 va_alist在VC中可以原樣給出,也可以略去,但在UNIX上的CC或GCC中都要省略掉。
此外,採用標頭檔stdarg.h編寫的程式是符合ANSI標準的,可以在各種作業系統和硬體上運行;而採用標頭檔varargs.h的方式僅僅是為了與以前的程式相容。所以建議大家使用前者。以下主要就前一種方式對參數的處理做出說明。兩種方式的基本原理是一致的,只是在文法形式上有一些細微的區別。
va_start使argp指向第一個選擇性參數。va_arg返回參數列表中的當前參數並使argp指向參數列表中的下一個參數。va_end把 argp指標清為NULL。函數體內可以多次遍曆這些參數,但是都必須以va_start開始,並以va_end結尾。
調用者在實際調用參數個數可變的函數時,要通過一定的方法指明實際參數的個數,例如把最後一個參數置為空白字串(系統調用execl()就是這樣的)、-1或其他的方式(函數printf()就是通過第一個參數,即輸出格式的定義來確定實際參數的個數的)。
下面給出一個具體的例子。前一部分是採用了符合ANSI標準的形式的代碼,後一部分是採用了與UNIX System V相容方式的代碼。代碼中加了一些注釋,這裡就不再解釋了。該例子已經在VC/Windows NT4.0、CC/AIX4.3.2.0、GCC/Redhat Linux 6.0環境下編譯並正常運行。
1、示範如何使用參數個數可變的函數,採用ANSI標準形式
#include < stdio.h >
#include < string.h >
#include < stdarg.h >
/* 函數原型聲明,至少需要一個確定的參數,
注意括弧內的省略符號 */
int demo( char *, ... );
void main( void )
{
demo("DEMO", "This", "is", "a", "demo!", "\0");
}
/* ANSI標準形式的聲明方式,括弧內的省略符號表示選擇性參數 */
int demo( char *msg, ... )
{
va_list argp; /* 定義儲存函數參數的結構 */
int argno = 0; /* 紀錄參數個數 */
char *para; /* 存放取出的字串參數 */
/* argp指向傳入的第一個選擇性參數,
msg是最後一個確定的參數 */
va_start( argp, msg );
while (1) {
para = va_arg( argp, char *); /*
取出當前的參數,類型為char *. */
if ( strcmp( para, "\0") == 0 )
/* 採用空串指示參數輸入結束 */
break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end( argp ); /* 將argp置為NULL */
return 0;
}
2、示範如何使用參數個數可變的函數,採用與UNIX System V相容的方式
#include < stdio.h >
#include < string.h >
#include < varargs.h >
/* 函數原型聲明,括弧內的類型va_list在
VC/Windows NT4.0可以保留,
但在AIX和Linux下需要去掉,即改成int demo( ) */
int demo( va_list );
void main( void )
{
demo("This", "is", "a", "demo!", "\0");
}
/* UNIX System V採用的聲明方式,括弧內是
va_alist,不是va_list,而且
va_dcl後面不需要分號 */
int demo( va_alist )
va_dcl
{
va_list argp;
/* 定義儲存函數參數的結構 */
int argno = 0;
/* 紀錄參數個數 */
char *para;
/* 存放取出的字串參數 */
va_start( argp );
/* argp指向第一個選擇性參數 */
while (1) {
para = va_arg( argp, char *);
/* 取出當前的參數,類型為char* */
if ( strcmp( para, "\0") == 0 )
/* 採用空串指示參數輸入結束 */
break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end( argp );
/* 將argp置為NULL */
return 0;
}
http://www.cnblogs.com/dongzhiquan/archive/2010/01/07/1994678.html