發現很多現代的語言中都有println這個函數,用起來很是方便,不用每次打印都要加上"\n",自己也實現了一個,方便平時調試的時候用。
#include <stdarg.h>
int println(const char *fmt, ...)
{
char printf_buf[1024];
va_list args;
int printed;
va_start(args, fmt);
printed = vsprintf(printf_buf, fmt, args);
va_end(args);
puts(printf_buf);
return printed;
}
這里用到了C語言的可變參函數,就順便談談C語言的可變參函數吧。所謂可變參函數即函數的參數可以是不定個數。上面我們已經看到了變參函數的定義方法,用形如int println(const char *fmt, ...)
來定義可變參函數。
注意:上例中的第一個參數fmt
是不可省略的,可變參函數至少需要一個普通的形參。
C語言之所以支持可變參函數,一個重要的原因是C調用規范中規定了C語言函數調用時,參數入棧的書序是從右往左,這意味着,棧頂的參數是第一個參數。這樣,被調用的函數,就不需要關心調用者會傳遞幾個參數進來,只要關心自己用到幾個參數既可以了。
例如這樣一個調用
查看函數的匯編代碼
可以看到,對println("%d %d", 10, 20, 30, 40);
的調用確實是從右往左將參數壓入到棧中。
這里還有另外一個問題是:因為可變參函數,函數定義的時候並沒有定義形參原型,調用的時候怎么使用參數呢?為此,C語言定義了如下的宏:
void va_start(va_list ap, last);//取第一個可變參數(如上述printf中的i)的指針給ap,last是函數聲明中的最后一個固定參數(比如printf函數原型中的*fromat);
type va_arg(va_list ap, type);//返回當前ap指向的可變參數的值,然后ap指向下一個可變參數;type表示當前可變參數的類型(支持的類型位int和double);
void va_end(va_list ap);//將ap置為NULL
其在頭文件中的定義如下:
/*
* define a macro to compute the size of a type, variable or expression,
* rounded up to the nearest multiple of sizeof(int). This number is its
* size as function argument (Intel architecture). Note that the macro
* depends on sizeof(int) being a power of 2!
*/
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _VA_LIST char*
typedef _VA_LIST va_list;
#define va_dcl va_list va_alist;
#define va_start(ap) ap = (va_list)&va_alist
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ap = (va_list)0
從上面的定義很容易可以看出,va_start
使ap
指向第一個可選參數。va_arg
返回參數列表中的當前參數並使ap
指向參數列表中的下一個參數。va_end
則將ap
置為空指針。
關於_INTSIZEOF
做一點簡單的解釋:因為sizeof(int)
是2^n
,因此它的位模式必然是1...000
,因此該宏會