函數中的可變參數傳遞原理

 
基本知識:

   我們經常在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;
       }
運行結果:
 

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