網路城邦
上一篇 回創作列表 下一篇   字體:
《教學》不定參數的 C 函式
2007/02/01 14:21:48瀏覽8523|回應11|推薦7
只要寫過 C 程式的人,都用過 printf 這個函式,也都知道它可是一個不定參數的函式,了解它的運作方式,你也可以實作自己的不定參數的函式。

先看一下 printf 的原型:int printf( const char *format [, argument]... );
在format後面的參數不但不定個數且型態也是不定的。這些參數是one bye one 的堆在stack裡面。
printf這個函式如何正確的取出這些參數?秘訣就在 format,函式根據format裡的格式("%d %f...")
依序把堆在stack裡的參數取出。而這取出的動作就是用到va_arg, va_end, va_start這三個macro再加上va_list。va_list 事實上是一個 char * 的型態:
typedef char * va_list;

其它的三個macro的例子(不同的平台會有不太一樣的macro)
/* A guess at the proper definitions for other platforms */
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )

這三個macro若看得懂,對了解它們的運作會很有幫助;看不太懂也沒關係,看一下下面的說明也差不可以了。首先 va_start 把取出的指標(va_list)指到第一個不定參數。
然後就可以用 va_arg 以指定的型態從va_list取出資料並把va_list指標移到下一個位置(例如取走了一個int 4 bytes 的資料, va_list便會加 4)
當取完資料後便可使用 va_end 把 va_list 歸零(這個macro應看得懂吧)。
其實對大部份的應用來說 va_end 做不做是沒太大的關係的。

下面是一個不定參數函式的範例(轉載自MSDN):
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;

va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}

註:_INTSIZEOF 是 int size memory alignment,如果你不知那是什麼就把 _INTSIZEOF 看成這樣的定義會比較容易理解:
#define _INTSIZEOF(n) sizeof(n)
為什麼不直接用sizeof(n)就好了,使用 macro 是為了跨平台時能保持運作正確。若傳了一個2 bytrs的不定參數(如 short),但在一個 32 bits的平台推入堆疊就會是4個bytes(32 bits)不是2 bytes,所以參數的取出就要非常的小心。使用_INTSIZEOF 在於保證引數指標移動的正確。
( 休閒生活旅人手札 )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=keithmin&aid=706363

 回應文章 頁/共 2 頁  回應文章第一頁 回應文章上一頁 回應文章下一頁 回應文章最後一頁

陳小春
等級:6
留言加入好友
為什麼 stdarg.h 與 varargs.h 裡的定義不同:
2007/02/01 14:34
不同平台本來就有其不同特性,使得相關的macro定義不同。有興趣,你得去了解varargs.h適用的平台(mac or win32)才能理解其相應的 macro。
頁/共 2 頁  回應文章第一頁 回應文章上一頁 回應文章下一頁 回應文章最後一頁