在
c語言中,有一種引數個數、型別不固定的函式,稱之為變參函式,比如常用的
printf
函式。當我們在輸出
log資訊時
,也希望能寫乙個變參函式作為介面。這裡介紹下如何寫變參函式。
一、引數巨集
先來看幾個設計變參函式要用到的幾個巨集,這幾個巨集定義在
stdarg.h
檔案中。
typedef char * va_list;
#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 ) 1
)指標型別
va_list
是指向變參的指標的型別。2)
va_start
用來初始化ap(
va_list
型),從該巨集的內容可看出
ap指向了
v後面的第乙個引數,通常呼叫此巨集使
ap指向第乙個變參。3)
va_arg
是將ap
指向下乙個引數,t為型別,該表示式返回下乙個引數的值。4)
va_end
是將ap置0
。二、sprintf
的實現我粗略地仿
sprintf
寫了個變參函式
mysprintf
,只為讓大家了解一下實現細節。
code:
int myprintf(const char* fmt, ...)
else }
count++;
fmt++; }
va_end(ap);
return count; }
注意:上面的**只是對
sprintf
部分格式的簡單模仿,並不能代替
sprintf
函式。
三、優化變參函式
下面從兩方面入手優化變參函式:
1、安全性方面 雖然
sprintf
輸出字串記憶體分配大小由呼叫者來控制,但免不了出現失控的局面,極易造成記憶體越界。
下面就是造成記憶體越界的情況:
char str[20];
sprintf(str, 「%d*%d = %d」, a, a, a*a); 當
a=10
時不會引起越界,當
a=10000
時長度越界。
解決方法是為輸出引數指明長度:
int snprintf(char *str, size_t size, const char *format, ...);
2、封裝變參控制邏輯
下面對變參控制部分進行了封裝。
code:
int hzy_snprintf(char* szout, size_t size, const char* fmt, ...)
int ret;
memset(szout, 0, size);
va_list ap;
va_start(ap, fmt);
#ifdef _win32
ret = _vsnprintf(szout, size, fmt, ap);
#else
ret = vsnprintf(szout, size, fmt, ap);
#endif
va_end(ap);
return ret; }
上面函式中使用了庫函式vsnprintf,函式原型:
int vsnprintf( char *buffer, size_t count, const char *format, va_list argptr ); 注:
_vsnprintf
函式是windows
平台專用函式,
vsnprintf
函式是c標準庫函式。使用了它,再也不用理會繁瑣的格式控制了。
上面的封裝還沒有結束。
本著提高**復用性的原則,欲將上面**應用於所有變參函式,但
當遇到另外乙個變參函式呼叫此函式時遇到了傳遞變參的問題。我們看到,只要把
fmt傳遞給子函式就可以做到變參部分的封裝,同時要注意
va_start
要取得fmt
第一種方法:使用雙指標或引用:
如:int __hzy_snprintf(char* szout, size_t size, const char** fmt)//
雙指標int __hzy_snprintf(char* szout, size_t size, const char* &fmt)//
引用使用雙指標的code如下(使用引用方式的code略):
int __hzy_snprintf(char* szout, size_t size, const char** fmt)
int ret;
memset(szout, 0, size);
va_list ap;
va_start(ap, *fmt);
#ifdef _win32
ret = _vsnprintf(szout, size, *fmt, ap);
#else
ret = vsnprintf(szout, size, *fmt, ap);
#endif
va_end(ap);
return ret; }
int hzy_snprintf(char* szout, size_t size, const char* fmt, ...)
return __snprintf(szout, size, &fmt); }
第二種方法:使用函式巨集
code:
#ifdef _win32
#define hzy_vsnprintf _vsnprintf
#else
#define hzy_vsnprintf vsnprintf
#endif
#define hzy_snprintf(szout, size, fmt) / /
int ret; /
memset(szout, 0, size); /
va_list ap;/
va_start(ap, fmt); /
ret = hzy_vsnprintf(szout, size, fmt, ap); /
va_end(ap); /
return ret; / }
int hzy_snprintf(char* szout, size_t size, const char* fmt, ...)
hzy_snprintf(szout, size, fmt); }
到這裡關於
c語言變參函式的實現說完了,上面所有**在
vc6上編譯通過。
c語言實現函式可變引數
c語言實現函式可變引數 title count 票 percent 前言 本文在很大程度上改編自kevintz的 c語言中可變引數的用法 一文,在行文之前先向這位前輩表示真誠的敬意和感謝。一 什麼是可變引數 我們在c語言程式設計中有時會遇到一些引數個數可變的函式,例如printf 函式,其函式原型為...
實現c語言中的可變引數函式
c語言程式設計中有時會遇到一些引數個數可變的函式,例如printf 函式,其函式原型為 int printf const char format,它除了有乙個引數format固定以外,後面跟的引數的個數和型別是可變的 用三個點 做引數佔位符 實際呼叫時可以有以下的形式 printf d i prin...
C語言中可變引數函式的實現
c語言的可變引數函式的實現需要使用標頭檔案stdarg.h,在該標頭檔案中定義了乙個變數型別va list和三個巨集va start va arg va end 下面將在 中講解這幾個巨集的使用方法。第一種方法是在函式內部手動指定可變引數的型別。首先需要知道可變引數的個數,並作為第乙個引數傳入。由於...