版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、C語言可變參數(shù)詳解1,首先,怎么得到參數(shù)的值。對于一般的函數(shù),我們可以通過參數(shù)對應(yīng)在參數(shù)列表里的標(biāo)識(shí)符來得到。但是參數(shù)可變函數(shù)那些可變的參數(shù)是沒有參數(shù)標(biāo)識(shí)符的,它只有“”,所以通過標(biāo)識(shí)符來得到是不可能的,我們只有另辟途徑。我們知道函數(shù)調(diào)用時(shí)都會(huì)分配??臻g,而函數(shù)調(diào)用機(jī)制中的棧結(jié)構(gòu)如下圖所示:|返回地址|調(diào)用函數(shù)運(yùn)行狀態(tài)|可見,參數(shù)是連續(xù)存儲(chǔ)在棧里面的,那么也就是說,我們只要得到可變參數(shù)的前一個(gè)參數(shù)的地址,就可以通過指針訪問到那些可變參數(shù)。但是怎么樣得到可變參數(shù)的前一個(gè)參數(shù)的地址呢?不知道你注意到?jīng)]有,參數(shù)可變函數(shù)在可變參數(shù)之前必有一個(gè)參數(shù)是固定的,并使用標(biāo)識(shí)符,而且通常被聲明為char*類型,
2、printf函數(shù)也不例外。這樣的話,我們就可以通過這個(gè)參數(shù)對應(yīng)的標(biāo)識(shí)符來得到地址,從而訪問其他參數(shù)變得可能。我們可以寫一個(gè)測試程序來試一下:#includevoidva_test(char*fmt,);/參數(shù)可變的函數(shù)聲明voidmain()inta=1,c=55;charb=b;va_test(,a,b,c);/用四個(gè)參數(shù)做測試voidva_test(char*fmt,)參數(shù)可變的函數(shù)定義,注意第一個(gè)參數(shù)為char*fmtchar*p=NULL;|p=(char*)&fmt;/注意不是指向fmt,而是指向&fmt,并且強(qiáng)制轉(zhuǎn)化為char*,以便一個(gè)一個(gè)字節(jié)訪問for(inti=0;i2 .L
3、inuxman手冊;3 .x86匯編,還有一些安全編碼方面的資料。轉(zhuǎn)帖對C/C+可變參數(shù)表的深層探索C/C+語言有一個(gè)不同于其它語言的特性,即其支持可變參數(shù),典型的函數(shù)如printf、scanf等可以接受數(shù)量不定的參數(shù)。如:printf(Iloveyou);printf(%d,a);printf(%d,%d,a,b);第一、二、三個(gè)printf分別接受1、2、3個(gè)參數(shù),讓我們看看printf函數(shù)的原型:intprintf(constchar*format,);從函數(shù)原型可以看出,其除了接收一個(gè)固定的參數(shù)format以外,后面的參數(shù)用表示。在C/C+語言中,表示可以接受不定數(shù)量的參數(shù),理論上來講
4、,可以是0或0以上的n個(gè)參數(shù)本文將對C/C+可變參數(shù)表的使用方法及C/C+支持可變參數(shù)表的深層機(jī)理進(jìn)行探索一.可變參數(shù)表的用法1、相關(guān)宏標(biāo)準(zhǔn)C/C+包含頭文件stdarg.h,該頭文件中定義了如下三個(gè)宏:voidva_start(va_listarg_ptr,prev_param);/返回值:求得的最大整數(shù)*/int max ( int num, .)int m = -0x7FFFFFFF; /* 32 系統(tǒng)中最小的整數(shù) */ va_list ap;va_start ( ap, num );for ( int i= 0; i num; i+ )/ .可以同數(shù)字一起顯示,需設(shè)置標(biāo)志(d、l、x、
5、s)/extern void DrawText ( BYTE xPos, BYTE yPos, LPBYTE lpStr, .)BYTE lpData100; / 緩沖區(qū) BYTE byIndex;ANSIversion*/typeva_arg(va_listarg_ptr,type);voidva_end(va_listarg_ptr);在這些宏中,va就是variableargument(可變參數(shù))的意思;arg_ptr是指向可變參數(shù)表的指針;prevparam則指可變參數(shù)表的前一個(gè)固定參數(shù);type為可變參數(shù)的類型。valist也是一個(gè)宏,其定義為typedefchar*valist,實(shí)
6、質(zhì)上是一char型指針。char型指針的特點(diǎn)是+、操作對其作用的結(jié)果是增1和減1(因?yàn)閟izeof(char)為1),與之不同的是int等其它類型指針的+、-操作對其作用的結(jié)果是增sizeof(type)或減sizeof(type),而且sizeof(type)大于1。通過va_start宏我們可以取得可變參數(shù)表的首指針,這個(gè)宏的定義為:#defineva_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v)顯而易見,其含義為將最后那個(gè)固定參數(shù)的地址加上可變參數(shù)對其的偏移后賦值給ap,這樣ap就是可變參數(shù)表的首地址。其中的INTSIZEOF宏定義為:#define_
7、INTSIZEOF(n)(sizeof(n)+sizeof(int)-1)&(sizeof(int)-1)va_arg宏的意思則指取出當(dāng)前arg_ptr所指的可變參數(shù)并將ap指針指向下一可變參數(shù),其原型為:#defineva_arg(list,mode)(mode*)(list=(char*)(int)list+(_builtin_alignof(mode)=4?3:7)&(_builtin_alignof(mode)=4?-4:-8)+sizeof(mode)-1對這個(gè)宏的具體含義我們將在后面深入討論。而va_end宏被用來結(jié)束可變參數(shù)的獲取,其定義為:#defineva_end(list)
8、可以看出,va_end(list)實(shí)際上被定義為空,沒有任何真實(shí)對應(yīng)的代碼,用于代碼對稱,與vastart對應(yīng);另外,它還可能發(fā)揮代碼的自注釋”作用。所謂代碼的自注釋,指的是代碼能自己注釋自己。下面我們以具體的例子來說明以上三個(gè)宏的使用方法。2、一個(gè)簡單的例子#include/*函數(shù)名:max*功能:返回n個(gè)整數(shù)中的最大值*參數(shù):num:整數(shù)的個(gè)數(shù):num個(gè)輸入的整數(shù)intt=va_arg(ap,int);if(tm)m=t;va_end(ap);returnm;/*主函數(shù)調(diào)用max*/intmain(intargc,char*argv口)intn=max(5,5,6,3,8,5);/*求5個(gè)
9、整數(shù)中的最大值*/coutn;return0;函數(shù)max中首先定義了可變參數(shù)表指針ap,而后通過va_start(ap,num)取得了參數(shù)表首地址(賦給了ap),其后的for循環(huán)則用來遍歷可變參數(shù)表。這種遍歷方式與我們在數(shù)據(jù)結(jié)構(gòu)教材中經(jīng)??吹降谋闅v方式是類似的。函數(shù)max看起來簡潔明了,但是實(shí)際上printf的實(shí)現(xiàn)卻遠(yuǎn)比這復(fù)雜。max函數(shù)之所以看起來簡單,是因?yàn)椋?1) max函數(shù)可變參數(shù)表的長度是已知的,通過num參數(shù)傳入;(2) max函數(shù)可變參數(shù)表中參數(shù)的類型是已知的,都為int型。而printf函數(shù)則沒有這么幸運(yùn)。首先,printf函數(shù)可變參數(shù)的個(gè)數(shù)不能輕易的得到,而可變參數(shù)的類型也不
10、是固定的,需由格式字符串進(jìn)行識(shí)別(由f、d、s等確定),因此則涉及到可變參數(shù)表的更復(fù)雜應(yīng)用。下面我們以實(shí)例來分析可變參數(shù)表的高級(jí)應(yīng)用。2 .高級(jí)應(yīng)用下面這個(gè)程序是我們?yōu)槟城度胧较到y(tǒng)(該系統(tǒng)中CPU的字長為16位)編寫的在屏幕上顯示格式字符串的函數(shù)DrawText,它的用法類似于intprintf(constchar*format,.)函數(shù),但其輸出的目標(biāo)為嵌入式系統(tǒng)的液晶顯示屏幕(LED)。/函數(shù)名稱:DrawText/功能說明:在顯示屏上繪制文字/參數(shù)說明:xPos-橫坐標(biāo)的位置0.30/yPos縱坐標(biāo)的位置0.64BYTEbyLen;DWORDdwTemp;WORDwTemp;inti;v
11、a_listIpParam;memset(IpData,0,100);byLen=strlen(lpStr);byIndex=0;|va_start(lpParam,lpStr);for(i=0;ibyLen;i+)if(lpStri!=喔是格式符開始lpDatabyIndex+=lpStri;elseswitch(lpStri+1)/整型cased:caseU:wTemp=va_arg(lpParam,int);byindex+=IntToStr(IpData+bylndex,(DWORD)wTemp);i+;break;/長整型casel:caseL:dwTemp=va_arg(lpPar
12、am,long);byindex+=IntToStr(lpData+byindex,(DWORD)dwTemp);i+;break;/16進(jìn)制(長整型)casex:caseX:dwTemp=va_arg(lpParam,long);byindex+=HexToStr(lpData+bylndex,(DWORD)dwTemp);i+;break;default:lpDatabyindex+=lpStri;|break;va_end(IpParam);lpDatabyindex=0;DisplayString(xPos,yPos,IpData,TRUE);/在屏幕上顯示字符串IpData在這個(gè)函數(shù)
13、中,需通過對傳入的格式字符串(首地址為IpStr)進(jìn)行識(shí)別來獲知可變參數(shù)個(gè)數(shù)及各個(gè)可變參數(shù)的類型,具體實(shí)現(xiàn)體現(xiàn)在for循環(huán)中。譬如,在識(shí)別為d后,做的是va_arg(IpParam,int),而獲知為l和x后則進(jìn)行的是vaarg(IpParam,long)。格式字符串識(shí)別完成后,可變參數(shù)也就處理完了。一在項(xiàng)目的最初,我們一直苦于不能找到一個(gè)好的辦法來混合輸出字符串和數(shù)字,我們采用了分別顯示數(shù)字和字符串的方法,并分別指定坐標(biāo),程序條理被破壞。而且,在混合顯示的時(shí)候,要給各類數(shù)據(jù)分別人工計(jì)算坐標(biāo),我們感覺頭疼不已。以前的函數(shù)為:/顯示字符串showString(BYTExPos,BYTEyPos,
14、LPBYTEIpStr)顯示數(shù)字showNum(BYTExPos,BYTEyPos,intnum)|以16進(jìn)制方式顯示數(shù)字_|showHexNum(BYTExPos,BYTEyPos,intnum)最終,我們用DrawText(BYTExPos,BYTEyPos,LPBYTEIpStr,.)函數(shù)代替了原先所有的輸出函數(shù),程序得到了簡化。就這樣,兄弟們用得爽翻了。3 .運(yùn)行機(jī)制探索通過第2節(jié)我們學(xué)會(huì)了可變參數(shù)表的使用方法,相信喜歡拋根問底的讀者還不甘心,必然想知道如下問題:(1)為什么按照第2節(jié)的做法就可以獲得可變參數(shù)并對其進(jìn)行操作?_(2)C/C+在底層究竟是依靠什么來對這一語法進(jìn)行支持的,為
15、什么其它語言就不能提供可變參數(shù)表呢?我們帶著這些疑問來一步步進(jìn)行摸索。3.1調(diào)用機(jī)制反匯編反匯編是研究語法深層特性的終極良策,先來看看2.2節(jié)例子中主函數(shù)進(jìn)行max(5,5,6,3,8,5)調(diào)用時(shí)的反匯編:1.004010c8push52. 004010CApush83. 004010CCpush34. 004010CEpush65. 004010D0push56. 004010D2push57. 004010D4callILT+5(max)(0040100a)從上述反匯編代碼中我們可以看出,C/C+函數(shù)調(diào)用的過程中:第一步:將參數(shù)從右向左入棧(第16行);第二步:調(diào)用call指令進(jìn)行跳轉(zhuǎn)(第
16、7行)。這兩步包含了深刻的含義,它說明C/C+默認(rèn)的調(diào)用方式為由調(diào)用者管理參數(shù)入棧的操作,且入棧的順序?yàn)閺挠抑磷螅@種調(diào)用方式稱為_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.00401020pushebp2. 00401021movebp,esp3. 00401023subesp,50h4. 00401026pushebx5. 00401027pushesi6. 00401028pushedi7. 00401029l
17、eaedi,ebp-50h8. 0040102Cmovecx,14h9. 00401031moveax,0CCCCCCCCh10. 00401036repstosdwordptrediva_listap;intm=-0x7FFFFFFF;/*32系統(tǒng)中最小的整數(shù)*/11. 00401038movdwordptrebp-8,80000001hva_start(ap,num);12. 0040103Fleaeax,ebp+0Ch13. 00401042movdwordptrebp-4,eaxfor(inti=0;im)28. 00401071moveax,dwordptrt29. 00401074
18、cmpeax,dwordptrebp-830. 00401077jlemax+5Fh(0040107f)m=t;31.00401079movecx,dwordptrt32. 0040107Cmovdwordptrebp-8,ecx33. 0040107Fjmpmax+2Eh(0040104e)va_end(ap);34. 00401081movdwordptrebp-4,0|returnm;35. 00401088moveax,dwordptrebp-836. 0040108Bpopedi37. 0040108Cpopesi38. 0040108Dpopebx39. 0040108Emovesp,ebp40. 00401090popebp41. 00401091ret分析上述反匯編代碼,對于一個(gè)真正的程序員而言,將是一種很大的享受;而對于初學(xué)者,也將使其受益良多。所以請一定要賴著頭皮認(rèn)真研究,千萬不要被嚇倒!行110進(jìn)行執(zhí)行函數(shù)內(nèi)代碼的準(zhǔn)備工作,保存現(xiàn)場。第2行對堆棧進(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é))。|第1213行非常關(guān)鍵,對應(yīng)著va_start(ap,num),這兩行將第一個(gè)可變參數(shù)的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- GB/T 29292-2024鞋類鞋類和鞋類部件中重點(diǎn)化學(xué)物質(zhì)管控指南
- Pemigatinib-d6-INCB054828-d-sub-6-sub-生命科學(xué)試劑-MCE-9553
- L-Pyroglutamic-acid-7-amido-4-methylcoumarin-生命科學(xué)試劑-MCE-3725
- Boc-Ala-Me-H117-生命科學(xué)試劑-MCE-9672
- 4-Fluoro-α-pyrrolidinopropiophenone-hydrochloride-生命科學(xué)試劑-MCE-5894
- 二零二五年度租賃期滿續(xù)租養(yǎng)老機(jī)構(gòu)居住協(xié)議合同
- 2025年度商鋪?zhàn)赓U協(xié)議終止及租賃場地使用權(quán)回購協(xié)議
- 二零二五年度茶餐廳股份合作經(jīng)營協(xié)議
- 2025年度智慧能源管理系統(tǒng)股東合作協(xié)議書
- 二零二五年度校園食堂檔口租賃合同與食品安全管理協(xié)議
- 2025年天津市政建設(shè)集團(tuán)招聘筆試參考題庫含答案解析
- 2024-2030年中國烘焙食品行業(yè)運(yùn)營效益及營銷前景預(yù)測報(bào)告
- 2025年上半年水利部長江水利委員會(huì)事業(yè)單位招聘68人(湖北武漢)重點(diǎn)基礎(chǔ)提升(共500題)附帶答案詳解
- (2024)云南省公務(wù)員考試《行測》真題及答案解析
- 寧德時(shí)代筆試題庫
- 五年級(jí)下冊北京版英語單詞
- 康復(fù)醫(yī)院患者隱私保護(hù)管理制度
- 新課標(biāo)I、Ⅱ卷 (2024-2020) 近五年高考英語真題滿分作文
- 浙江省嘉興市2023-2024學(xué)年六年級(jí)(上)期末數(shù)學(xué)試卷
- 子宮脫垂手術(shù)指南
- 沈陽理工大學(xué)《數(shù)》2022-2023學(xué)年第一學(xué)期期末試卷
評(píng)論
0/150
提交評(píng)論