一、可變參數(shù)表介紹
c/c++語(yǔ)言具備一個(gè)不同于其他編程語(yǔ)言的的特性,即支持可變參數(shù)。
例如C庫(kù)中的printf,scanf等函數(shù),都支持輸入數(shù)量不定的參數(shù)。例如:
printf("helloworld");////1個(gè)參數(shù) prinf("%d",?a);?????????////2個(gè)參數(shù) printf("%d,?%d",?a,?b);?////3個(gè)參數(shù)
printf函數(shù)原型為 int printf(const char *format, …);
從printf的原型來(lái)看,其除了接受一個(gè)固定參數(shù)format以外,后面的參數(shù)使用…來(lái)表示。
在c/c++語(yǔ)言中,…表示可以接受不定數(shù)量的參數(shù)。
二、可變參數(shù)表用法
在標(biāo)準(zhǔn)C/C++中,頭文件中定義了如下三個(gè)宏:
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ù)表的前一個(gè)固定參數(shù)
type 為可變參數(shù)的類型
va_list 也是一個(gè)宏
其定義為typedef char * va_list 實(shí)質(zhì)上是一char 型指針。
char 型指針的特點(diǎn)是++、--操作對(duì)其作用的結(jié)果是增1 和減1(因?yàn)閟izeof(char)為1)。
與之不同的是int 等其它類型指針的++、--操作對(duì)其作用的結(jié)果是增sizeof(type)或減sizeof(type),而且sizeof(type)大于1。
通過(guò)使用va_start宏我們可以取得可變參數(shù)表的首指針,這個(gè)宏的定義為:
#defineva_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))
其作用為將最后那個(gè)固定參數(shù)的地址加上可變參數(shù)對(duì)其的偏移后賦值給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宏定義用來(lái)結(jié)束可變參數(shù)的獲取,定義為:
#defineva_end(list)
va_end ( list )實(shí)際上被定義為空,沒(méi)有任何真實(shí)對(duì)應(yīng)的代碼,用于代碼對(duì)稱,與va_start對(duì)應(yīng);
可能發(fā)揮代碼的“自注釋”作用。所謂代碼的“自注釋”,指的是代碼能自己注釋自己。
三、可變參數(shù)表的簡(jiǎn)單使用
#include#include #include /** *@brief求n個(gè)數(shù)中的最大值 *@details *@param[in]num整數(shù)個(gè)數(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個(gè)整數(shù)中的最大值*/ cout<
max(int num, …)中首先定義了可變參數(shù)表指針ap,而后通過(guò)va_start ( ap, num )取得了參數(shù)表首地址(賦給了ap),其后的for 循環(huán)則用來(lái)遍歷可變參數(shù)表。
max函數(shù)相比于printf簡(jiǎn)單了許多,其原因如下:
max函數(shù)可變參數(shù)表的長(zhǎng)度是已知的,通過(guò)num參數(shù)傳入;
max函數(shù)可變參數(shù)表中參數(shù)的類型是已知的,都為int型;
printf 函數(shù)可變參數(shù)的個(gè)數(shù)不能輕易的得到,而可變參數(shù)的類 型也不是固定的,需由格式字符串進(jìn)行識(shí)別(由%f、%d、%s 等確定)。
四、運(yùn)行機(jī)制
反匯編是研究語(yǔ)法深層特性的終極良策,首先查看main函數(shù)中調(diào)用max函數(shù)時(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 指令進(jìn)行跳轉(zhuǎn)(第7行)
這兩步包含了深刻的含義,它說(shuō)明C/C++默認(rèn)的調(diào)用方式為由調(diào)用者管理參數(shù)入棧的操作,且入棧的順序?yàn)閺挠抑磷螅@種調(diào)用方式稱為_(kāi)cdecl調(diào)用。
x86系統(tǒng)的入棧方向?yàn)閺母叩刂返降偷刂?,故?至n個(gè)參數(shù)被放在了地址遞增的堆棧內(nèi)。在被調(diào)用函數(shù)內(nèi)部,讀取這些堆棧的內(nèi)容就可獲得各個(gè)參數(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行進(jìn)行執(zhí)行函數(shù)內(nèi)代碼的準(zhǔn)備工作,保存現(xiàn)場(chǎng);
第2行對(duì)堆棧進(jìn)行移動(dòng);
第3行則意味著max函數(shù)為其內(nèi)部局部變量準(zhǔn)備的堆??臻g為50h字節(jié);
第11行表示把變量n 的內(nèi)存空間安排在了函數(shù)內(nèi)部局部棧底減8的位置(占用4個(gè)字節(jié));
第12~13行非常關(guān)鍵,對(duì)應(yīng)著va_start ( ap, num),這兩行將第一個(gè)可變參數(shù)的地址賦值給了指針ap;
從第12行可以看出num 的地址為ebp+0Ch;
從第13行可以看出ap 被分配在函數(shù)內(nèi)部局部棧底減4 的位置上(占用4 個(gè)字節(jié));
第22~27行最為關(guān)鍵,對(duì)應(yīng)著va_arg (ap, int);
第22~24行的作用為將ap 指向下一可變參數(shù)(可變參數(shù)的地址間隔為4 個(gè)字節(jié),從add eax,4 可以看出);
第25~27行則取當(dāng)前可變參數(shù)的值賦給變量t。這段反匯編很奇怪,它先移動(dòng)可變參數(shù)指針,再在賦值指令里面回過(guò)頭來(lái)取先前的參數(shù)值賦給t(從mov edx,dword ptr [ecx-4]語(yǔ)句可以看出);
第36~41行恢復(fù)現(xiàn)場(chǎng)和堆棧地址,執(zhí)行函數(shù)返回操作。
審核編輯:湯梓紅
-
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7628瀏覽量
139754 -
編程語(yǔ)言
+關(guān)注
關(guān)注
10文章
1952瀏覽量
35771 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4365瀏覽量
63872 -
C++
+關(guān)注
關(guān)注
22文章
2116瀏覽量
74586 -
可變參數(shù)
+關(guān)注
關(guān)注
0文章
2瀏覽量
4861
原文標(biāo)題:C語(yǔ)言可變參數(shù)的使用詳解
文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
C語(yǔ)言可變形參是什么
可變參數(shù)函數(shù)的實(shí)現(xiàn)原理
C語(yǔ)言——可變參數(shù)問(wèn)題.
C語(yǔ)言中可變參數(shù)的定義
怎么設(shè)計(jì)c語(yǔ)言的可變長(zhǎng)參數(shù)函數(shù)?
C++ 語(yǔ)言命令詳解(第二版)
單片機(jī)C語(yǔ)言和匯編語(yǔ)言混合編程實(shí)例詳解
C語(yǔ)言-函數(shù)的可變形參(不定形參)
c語(yǔ)言帶參數(shù)的宏定義
C語(yǔ)言中的可變參數(shù)介紹

評(píng)論