一、可變參數(shù)表介紹
c/c++語言具備一個不同于其他編程語言的的特性,即支持可變參數(shù)。
例如C庫中的printf,scanf等函數(shù),都支持輸入數(shù)量不定的參數(shù)。例如:
printf("helloworld");////1個參數(shù) prinf("%d",?a);?????????////2個參數(shù) printf("%d,?%d",?a,?b);?////3個參數(shù)
printf函數(shù)原型為 int printf(const char *format, …);
從printf的原型來看,其除了接受一個固定參數(shù)format以外,后面的參數(shù)使用…來表示。
在c/c++語言中,…表示可以接受不定數(shù)量的參數(shù)。
二、可變參數(shù)表用法
在標(biāo)準(zhǔn)C/C++中,頭文件中定義了如下三個宏:
voidva_start(va_listarg_ptr,prev_param);/*ANSIversion*/ typeva_arg(va_listarg_ptr,type); voidva_end(va_listarg_ptr);
va 就是variable argument(可變參數(shù))的意思
arg_ptr 是指向可變參數(shù)表的指針
prev_param 則指可變參數(shù)表的前一個固定參數(shù)
type 為可變參數(shù)的類型
va_list 也是一個宏
其定義為typedef char * va_list 實質(zhì)上是一char 型指針。
char 型指針的特點是++、--操作對其作用的結(jié)果是增1 和減1(因為sizeof(char)為1)。
與之不同的是int 等其它類型指針的++、--操作對其作用的結(jié)果是增sizeof(type)或減sizeof(type),而且sizeof(type)大于1。
通過使用va_start宏我們可以取得可變參數(shù)表的首指針,這個宏的定義為:
#defineva_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))
其作用為將最后那個固定參數(shù)的地址加上可變參數(shù)對其的偏移后賦值給ap,這樣ap就是可變參數(shù)表的首地址。
_INTSIZEOF 宏定義為:
#define_INTSIZEOF(n)((sizeof(n)+sizeof(int)–1)&~(sizeof(int)–1))
宏定義va_arg原型為:
#defineva_arg(list,mode)((mode*)(list= (char*)((((int)list+(__builtin_alignof(mode)<=4?3:7))?& (__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]
其作用為指取出當(dāng)前arg_ptr 所指的可變參數(shù)并將ap 指針指向下一可變參數(shù)。
va_end宏定義用來結(jié)束可變參數(shù)的獲取,定義為:
#defineva_end(list)
va_end ( list )實際上被定義為空,沒有任何真實對應(yīng)的代碼,用于代碼對稱,與va_start對應(yīng);
可能發(fā)揮代碼的“自注釋”作用。所謂代碼的“自注釋”,指的是代碼能自己注釋自己。
三、可變參數(shù)表的簡單使用
#include#include #include /** *@brief求n個數(shù)中的最大值 *@details *@param[in]num整數(shù)個數(shù) *@param[out]...整數(shù) *@retval最大整數(shù) *@par */ intmax(intnum,...){ intm=-0x7FFFFFFF;/*32系統(tǒng)中最小的整數(shù)*/ va_listap; va_start(ap,num); for(inti=0;im){ m=t; } } va_end(ap); returnm; } intmain(intargc,char*argv[]){ intn=max(5,5,6,3,8,5);/*求5個整數(shù)中的最大值*/ cout<
max(int num, …)中首先定義了可變參數(shù)表指針ap,而后通過va_start ( ap, num )取得了參數(shù)表首地址(賦給了ap),其后的for 循環(huán)則用來遍歷可變參數(shù)表。
max函數(shù)相比于printf簡單了許多,其原因如下:
max函數(shù)可變參數(shù)表的長度是已知的,通過num參數(shù)傳入;
max函數(shù)可變參數(shù)表中參數(shù)的類型是已知的,都為int型;
printf 函數(shù)可變參數(shù)的個數(shù)不能輕易的得到,而可變參數(shù)的類 型也不是固定的,需由格式字符串進行識別(由%f、%d、%s 等確定)。
四、運行機制
反匯編是研究語法深層特性的終極良策,首先查看main函數(shù)中調(diào)用max函數(shù)時的反匯編:
1.004010C8push5 2.004010CApush8 3.004010CCpush3 4.004010CEpush6 5.004010D0push5 6.004010D2push5 7.004010D4call@ILT+5(max)(0040100a)
第一步:將參數(shù)從右向左入棧(第1~6行)
第二步:調(diào)用call 指令進行跳轉(zhuǎn)(第7行)
這兩步包含了深刻的含義,它說明C/C++默認(rèn)的調(diào)用方式為由調(diào)用者管理參數(shù)入棧的操作,且入棧的順序為從右至左,這種調(diào)用方式稱為_cdecl調(diào)用。
x86系統(tǒng)的入棧方向為從高地址到低地址,故第1至n個參數(shù)被放在了地址遞增的堆棧內(nèi)。在被調(diào)用函數(shù)內(nèi)部,讀取這些堆棧的內(nèi)容就可獲得各個參數(shù)的值,讓我們反匯編到max函數(shù)的內(nèi)部。
intmax(intnum,...){ 1.00401020pushebp 2.00401021movebp,esp 3.00401023subesp,50h 4.00401026pushebx 5.00401027pushesi 6.00401028pushedi 7.00401029leaedi,[ebp-50h] 8.0040102Cmovecx,14h 9.00401031moveax,0CCCCCCCCh 10.00401036repstosdwordptr[edi] va_listap; intm=-0x7FFFFFFF;/*32系統(tǒng)中最小的整數(shù)*/ 11.00401038movdwordptr[ebp-8],80000001h va_start(ap,num); 12.0040103Fleaeax,[ebp+0Ch] 13.00401042movdwordptr[ebp-4],eax for(inti=0;im) 28.00401071moveax,dwordptr[t] 29.00401074cmpeax,dwordptr[ebp-8] 30.00401077jlemax+5Fh(0040107f) m=t; 31.00401079movecx,dwordptr[t] 32.0040107Cmovdwordptr[ebp-8],ecx } 33.0040107Fjmpmax+2Eh(0040104e) va_end(ap); 34.00401081movdwordptr[ebp-4],0 returnm; 35.00401088moveax,dwordptr[ebp-8] } 36.0040108Bpopedi 37.0040108Cpopesi 38.0040108Dpopebx 39.0040108Emovesp,ebp 40.00401090popebp 41.00401091ret
第1~10行進行執(zhí)行函數(shù)內(nèi)代碼的準(zhǔn)備工作,保存現(xiàn)場;
第2行對堆棧進行移動;
第3行則意味著max函數(shù)為其內(nèi)部局部變量準(zhǔn)備的堆棧空間為50h字節(jié);
第11行表示把變量n 的內(nèi)存空間安排在了函數(shù)內(nèi)部局部棧底減8的位置(占用4個字節(jié));
第12~13行非常關(guān)鍵,對應(yīng)著va_start ( ap, num),這兩行將第一個可變參數(shù)的地址賦值給了指針ap;
從第12行可以看出num 的地址為ebp+0Ch;
從第13行可以看出ap 被分配在函數(shù)內(nèi)部局部棧底減4 的位置上(占用4 個字節(jié));
第22~27行最為關(guān)鍵,對應(yīng)著va_arg (ap, int);
第22~24行的作用為將ap 指向下一可變參數(shù)(可變參數(shù)的地址間隔為4 個字節(jié),從add eax,4 可以看出);
第25~27行則取當(dāng)前可變參數(shù)的值賦給變量t。這段反匯編很奇怪,它先移動可變參數(shù)指針,再在賦值指令里面回過頭來取先前的參數(shù)值賦給t(從mov edx,dword ptr [ecx-4]語句可以看出);
第36~41行恢復(fù)現(xiàn)場和堆棧地址,執(zhí)行函數(shù)返回操作。
審核編輯:湯梓紅
-
C語言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136685 -
編程語言
+關(guān)注
關(guān)注
10文章
1942瀏覽量
34707 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62569 -
C++
+關(guān)注
關(guān)注
22文章
2108瀏覽量
73618 -
可變參數(shù)
+關(guān)注
關(guān)注
0文章
2瀏覽量
4815
原文標(biāo)題:C語言可變參數(shù)的使用詳解
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論