文章目錄
- 【原型】
- 【描述】
- 【使用方法】
- 【實現分析】
- 【例子分析】
【原型】
type fun( type arg1, type arg2, ...
);
【描述】
主要用在參數個數不確定的函數中,例如:printf函數。
【使用方法】
參考:glib/manual/Add.c
#include <stdarg.h><br />#include <stdio.h><br />int add_em_up (int count,...)<br />{<br /> va_list ap;<br /> int i, sum;<br /> va_start (ap, count);/* Initialize the argument list. */<br /> sum = 0;<br /> for (i = 0; i < count; i++)<br /> sum += va_arg (ap, int);/* Get the next argument value. */<br /> va_end (ap);/* Clean up. */<br /> return sum;<br />}<br />int main (void)<br />{<br /> /* This call prints 16. */<br /> printf ("%d/n", add_em_up (3, 5, 5, 6));<br /> /* This call prints 55. */<br /> printf ("%d/n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));<br /> return 0;<br />}
運行結果:
16
55
【實現分析】
如果要實現可變參數,關鍵是我們怎樣遍曆函數列表
,這就牽扯到函數的參數鏈表在棧中的存放順序問題:
函數入棧的過程大致如下: (更詳細的內容參考這裡
)
- 函數的局部變數入棧;
- 其他被調函數資訊指標入棧;
- 函數返回地址入棧;
- 參數列表入棧,順序從右至左;
此時我們可以明確參數列表的存放順序,如下:
所以我們必須要知道棧頂參數地址,即函數的第一個參數;
主要依靠定義在stdarg.h中三個宏:
- void va_start( va_list arg_ptr, prev_param ); /*給出第一個參數的地址*/
- type va_arg( va_list arg_ptr, type ); /*從第二個參數開始取,每次返回一個*/
- void va_end( va_list arg_ptr ); /*標記已取完,其實什麼也不做*/
stdarg.h的實現和注釋如下:
#ifndef _STDARG_H<br />#define _STDARG_H<br />typedef char *va_list;<br />/*根據類型值做記憶體對齊*/<br />#define __va_rounded_size(TYPE) /<br /> (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))</p><p>/*根據第一參數LASTARG的地址,擷取第二參數地址*/<br />#define va_start(AP, LASTARG) (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))<br />/*已完成,無需做*/<br />#define va_end(AP)<br />/*根據TYPE,返回當前AP指向的參數值,AP指向下一個<br />這裡用到逗號運算式,先左後右,最右邊的是這個結果。<br />AP += __va_rounded_size (TYPE), 指向下一個參數;<br />(AP += __va_rounded_size (TYPE),返回上一個參數;<br />*/<br />#define va_arg(AP, TYPE) /<br />(AP += __va_rounded_size (TYPE), *((TYPE *) (AP - __va_rounded_size (TYPE))))<br />#endif /* _STDARG_H */
其實stdarg.h的實現會依據編譯器的不同有所差異,瞭解更多查看這裡
。
【例子分析】
這個例子我們可以修改一下glib/manual/Add.c 如下:add_ex.c
#include <stdarg.h><br />#include <stdio.h><br />int show_arg (int count,...)<br />{<br /> va_list ap;<br /> int i = 1;<br /> va_start (ap, count); /* 給出第一參數的地址 */<br /> printf("first arg address =%d/n",(unsigned int)ap-sizeof(count));<br /> for (i = 1; i <= count; i++)<br /> printf("%d = %d /t next_addre =%d/n",i,va_arg (ap, int),(unsigned int)ap); /*迴圈取下一個參數*/<br /> va_end (ap);/* 結束 */<br /> return 0;<br />}<br />int main (void)<br />{</p><p> /*我們把 count的值故意多設定一個,<br />這樣我們顯示第六個的值應該是函數的返回地址*/<br /> show_arg(10, 1, 2, 3, 4, 5);<br /> return 0;<br />}<br />
運行結果: GCC和vc的編譯結果有差異。
轉載請聲明來處:http://blog.csdn.net/oncoding/archive/2009/09/13/4549160.aspx