字體:小 中 大 | |
|
|
2007/02/01 14:21:48瀏覽8517|回應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 在於保證引數指標移動的正確。 |
|
( 休閒生活|旅人手札 ) |