建体彩网|中彩网双色球连号|
?
快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

和記娛樂備用:深入淺出VA函數的使用技巧

?

本文主要先容可變參數的函數應用,然后闡發它的道理,法度榜樣員自己若何對它們實現和封裝,著末是可能會呈現的問題和避免步伐。

VA函數(variable argument function),參數個數可變函數,又稱可變參數函數。C/C++編程中,系統供給給編程職員的va函數很少。*printf()/*scanf()系列函數,用于輸入輸出時款式化字符串;exec*()系列函數,用于在法度榜樣中履行外部文件(main(int argc,char*argv[]算不算呢,與其說main()也是一個可變參數函數,倒不如說它是exec*()顛末封裝后的具備特殊功能和意義的函數,至少在道理這一級上有很多相似之處)。因為參數個數的不確定,使va函數具有很大年夜的機動性,易用性,對沒有應用過可變參數函數的編程職員很有誘惑力;那么,該若何編寫自己的va函數,va函數的運用機會、編譯實現又是若何。作者借本文談談自己關于va函數的一些穴見。

一、 從printf()開始

從大年夜家都很認識的款式化字符串函數開始先容可變參數函數。

原型:int printf(const char * format, ...);

參數format表示若何來款式字符串的指令,…

表示可選參數,調用時通報給"..."的參數可有可無,根據實際環境而定。

系統供給了vprintf系列款式化字符串的函數,用于編程職員封裝自己的I/O函數。

int vprintf / vscanf(const char * format, va_list ap); // 從標準輸入/輸出款式化字符串

int vfprintf / vfsacanf(FILE * stream, const char * format, va_list ap); // 從文件流

int vsprintf / vsscanf(char * s, const char * format, va_list ap); // 從字符串

// 例1:款式化到一個文件流,可用于日志文件

FILE *logfile;

int WriteLog(const char * format, ...)

{

va_list arg_ptr;

va_start(arg_ptr, format);

int nWrittenBytes = vfprintf(logfile, format, arg_ptr);

va_end(arg_ptr);

return nWrittenBytes;

}

// 調用時,與應用printf()沒有差別。

WriteLog("%04d-%02d-%02d %02d:%02d:%02d %s/%04d logged out.",

nYear, nMonth, nDay, nHour, nMinute, szUserName, nUserID);

同理,也可以從文件中履行款式化輸入;或者對標準輸入輸出,字符串履行款式化。

在上面的例1中,WriteLog()函數可以吸收參數個數可變的輸入,本色上,它的實現必要vprintf()的支持。若何真正實現屬于自己的可變參數函數,包括節制每一個傳入的可選參數。

二、 va函數的定義和va宏

C說話支持va函數,作為C說話的擴展--C++同樣支持va函數,但在C++中并不保舉應用,C++引入的多態性同樣可以實現參數個數可變的函數。不過,C++的重載功能終究只能是有限多個可以預見的參數個數。對照而言,C中的va函數則可以定義無窮多個相稱于C++的重載函數,這方面C++是力所不及的。va函數的上風表現在應用的方便性和易用性上,可以使代碼更簡潔。C編譯器為了統一在不合的硬件架構、硬件平臺上的實現,和增添代碼的可移植性,供給了一系列宏來樊籬硬件情況不合帶來的差異。

ANSI C標準下,va的宏定義在stdarg.h中,它們有:va_list,va_start(),va_arg(),va_end()。

// 例2:求隨意率性個自然數的平方和:

int SqSum(int n1, ...)

{

va_list arg_ptr;

int nSqSum = 0, n = n1;

va_start(arg_p和記娛樂備用tr, n1);

while (n > 0)

{

nSqSum += (n * n);

n = va_arg(arg_ptr, int);

}

va_end(arg_ptr);

return nSqSum;

}

// 調用時

int nSqSum = S和記娛樂備用qSum(7, 2, 7, 11, -1);

可變參數函數的原型聲明款式為:

type VAFunction(type arg1, type arg2, … );

參數可以分為兩部分:個數確定的固定參數和個數可變的可選參數。函數至少必要一個固定參數,固定參數的聲明和通俗函數一樣;可選參數因為個數不確定,聲明時用"…"表示。固定參數和可選參數公同構成一個函數的參數列表。

這里,移動指針使其指向下一個參數,那么移動指針時的偏移量是若干呢,沒有詳細謎底,由于這里涉及到內存對齊(alignment)問題,內存對齊跟詳細應用的硬件平臺有親昵關系,比如大年夜家熟知的32位x86平臺規定所有的變量地址必須是4的倍數(sizeof(int) = 4)。va機制頂用宏_INTSIZEOF(n)來辦理這個問題,沒有這些宏,va的可移植性無從談起。

首先先容宏_INTSIZEOF(n),它求出變量占用內存空間的大年夜小,是va的實現的根基。

#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 )              // 將指針置為無效

下表是針對函數int TestFunc(int n1, int n2, int n3, …)

參數通報時的內存客棧環境。(C編譯器默認的參數通報要領是__cdecl。)

對該函數的調用為int result = TestFunc(a, b, c, d. e); 此中e為停止標志。

從上圖中可以很清楚地看出va_xxx宏如斯編寫的緣故原由。

1. va_start。為了獲得第一個可選參數的地址,我們有三種法子可以做到:

A) = &n3 + _INTSIZEOF(n3)

// 著末一個固定參數的地址 + 該參數占用內存的大年夜小

B) = &n2 + _INTSIZEOF(n3) + _INTSIZEOF(n2)

// 中心某個固定參數的地址 + 該參數之后所有固定參數占用的內存大年夜小之和

回顧到printf/scanf系列函數的%d %s之類的款式化指令,我們不難理解這些它們的用途了- 昭示參數強制轉換的類型。

(注:printf/scanf沒有應用va_xxx來實現,但道理是同等的。)

3.va_end很簡單,僅僅是把指針作廢而已。

#define va_end(ap) (ap = (va_list)0) // x86平臺

四、 簡潔、機動,也有危險

從va的實現可以看出,指針的合理運用,把C說話簡潔、機動的特點體現得淋漓盡致,叫人不得不佩服C的強大年夜和高效。弗成否認的是,給編程職員太多自由空間一定使法度榜樣的安然性低落。va中,為了獲得所有通報給函數的參數,必要用va_arg依次遍歷。此中存在兩個隱患:

1)若何確定參數的類型。

va_arg在類型反省和記娛樂備用方面與其說異常機動,不如說是很不認真,由于是強制類型轉換,va_arg都把當前指針所指向的內容強制轉換到指定類型;

2)停止標志。假如沒有停止標志的判斷,va將按默認和記娛樂備用類型依次返回內存中的內容,直到造訪到不法內存而掉足退出。例2中SqSum()求的是自然數的平方和,以是我把負數和0作為它的停止標志。例如scanf把接管到的回車符作為停止標志,大年夜家熟知的printf()對字符串的處置懲罰用'\0'作為停止標志,無法想象C中的字符串假如沒有'\0', 代碼將會是如何一番情景,預計那時最盛行的可能是字符數組,或者是malloc/free。

容許對內存的隨意造訪,會留給不懷美意者留下進擊的可能。當處置懲罰cracker精心設計好的一串字符串后,法度榜樣將跳轉到一些惡意代碼區域履行,以使cracker達到其進擊目的。(常見的exploit進擊)以是,必需禁止對內存的隨意造訪和嚴格節制內存造訪界限。

五、 Unix System V兼容要領的va聲明

上面先容可變參數函數的聲明是采納ANSI標準的,Unix System V兼容要領的聲明有一點點差別,它增添了兩個宏:va_alist,va_dcl。而且它們不是定義在stdarg.h中,而是varargs.h中。stdarg.h是ANSI標準的;varargs.h僅僅是為了能與曩昔的法度榜樣維持兼容而呈現的,現在的編程中不保舉應用。

免責聲明:以上內容源自網絡,版權歸原作者所有,如有侵犯您的原創版權請告知,我們將盡快刪除相關內容。

您可能還會對下面的文章感興趣:

建体彩网
青海十一选五今日预测杀码号 课堂直播怎么赚钱 2018海南环岛赛海口 大版六合皇 棒球比分多少算赢 深圳风采走势图100期 北京赛车最大的平台 4场进球最新开奖 河北快三计划精准版件 斗地主怎么玩