C語言可變引數的函式實現

2021-05-24 13:31:46 字數 3466 閱讀 1333

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 下面將在 中講解這幾個巨集的使用方法。第一種方法是在函式內部手動指定可變引數的型別。首先需要知道可變引數的個數,並作為第乙個引數傳入。由於...