基本知識:
我們經常在LINUX代碼中看到傳遞不定參數的函數,這是怎樣實現的呢?
我們可以通過在頭文件stdarg.h中定義的va_start(), va_arg(),va_end()這幾個函數來實現。在/opt/buildroot-gcc342/lib/gcc/mipsel-linux-uclibc/3.4.2 /include目錄下找到了stdarg.h,值得注意的是,這三個函數是在gcc編譯器中定義的,而不是內核或C庫中的定義的。
它們的作用是:
va_start
使argp指向第一個可選參數。注意是第一個可選參數,而不是第一個參數。
va_arg
返回參數列表中的當前參數並使argp指向參數列表中的下一個參數。
va_end
把argp指針清爲NULL。函數體內可以多次遍歷這些參數,但是都必須以va_start開始,並以va_end結尾。
基本原理
關於stdarg.h中的定義,我並沒有去深究。我想其基本原理可以用下面的這段代碼來說明。
#include <stdio.h>
void fun(int a, ...)
{
int *temp = &a;
temp++;
int i=0;
for(i = 0; i < a; ++i)
{
printf("%d\n", *temp);
temp++;
}
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
fun(4, a, b, c, d);
return 0;
}
stdarg.h與上面代碼不同之處,我的理解是,va_start,和va_end的作用,有點像進入臨界區的意思,在這個臨界區內,不允許其他進程的內存操作。
應用實例
例如,在我們是實際應用中goahead開源軟件中用到的這段代碼:
int websWrite(webs_t wp, char_t *fmt, ...)
{
va_list vargs;
char_t *buf;
int rc;
a_assert(websValid(wp));
va_start(vargs, fmt);
buf = NULL;
rc = 0;
if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
trace(0, T("webs: websWrite lost data, buffer overflow\n"));
}
va_end(vargs);
a_assert(buf);
if (buf) {
rc = websWriteBlock(wp, buf, gstrlen(buf));
bfree(B_L, buf);
}
return rc;
}
下面再給個例子。
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
/*函數原型聲明,至少需要一個確定的參數,注意括號內的省略號*/
int demo(char[], ...);
int main(void)
{
demo("DEMO", "This", "is","a", "demo!", "");
return 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 *);
if ( strcmp( para, "") == 0 )
break;
printf("Parameter #%d is:%s\n",argno,para);
argno++;
}
va_end( argp );
/*將argp置爲NULL*/
return 0;
}
運行結果: