www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號(hào)精選 > 嵌入式基地

可變參數(shù)表介紹

c/c++語言具備一個(gè)不同于其他編程語言的的特性,即支持可變參數(shù)。

例如C庫中的printf,scanf等函數(shù),都支持輸入數(shù)量不定的參數(shù)。例如:

printf("hello world"); ////< 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的原型來看,其除了接受一個(gè)固定參數(shù)format以外,后面的參數(shù)使用來表示。

在c/c++語言中,表示可以接受不定數(shù)量的參數(shù)。

可變參數(shù)表用法

在標(biāo)準(zhǔn)C/C++中,頭文件中定義了如下三個(gè)宏:

void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */ type va_arg ( va_list arg_ptr, type ); void va_end ( va_list arg_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。

通過使用va_start宏我們可以取得可變參數(shù)表的首指針,這個(gè)宏的定義為:

#define va_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原型為:

#define va_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ù)的獲取,定義為:

#define va_end ( list )
  • va_end ( list )實(shí)際上被定義為空,沒有任何真實(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 
 */ 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++ ) { int t = va_arg (ap, int); if ( t > m ) {
 m = t;
 }
 }
 va_end (ap); return m;
} int main ( int argc, char* argv[] ) { int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5 個(gè)整數(shù)中的最大值 */ cout << n; return 0;
}

max(int num, …)中首先定義了可變參數(shù)表指針ap,而后通過va_start ( ap, num )取得了參數(shù)表首地址(賦給了ap),其后的for 循環(huán)則用來遍歷可變參數(shù)表。

max函數(shù)相比于printf簡(jiǎn)單了許多,其原因如下:

  • max函數(shù)可變參數(shù)表的長(zhǎng)度是已知的,通過num參數(shù)傳入

  • max函數(shù)可變參數(shù)表中參數(shù)的類型是已知的,都為int型

  • printf 函數(shù)可變參數(shù)的個(gè)數(shù)不能輕易的得到,而可變參數(shù)的類 型也不是固定的,需由格式字符串進(jìn)行識(shí)別(由%f、%d、%s 等確定)

運(yùn)行機(jī)制

反匯編是研究語法深層特性的終極良策,首先查看main函數(shù)中調(diào)用max函數(shù)時(shí)的反匯編:

1. 004010C8 push 5 2. 004010CA push 8 3. 004010CC push 3 4. 004010CE push 6 5. 004010D0 push 5 6. 004010D2 push 5 7. 004010D4 call @ILT+5(max) (0040100a)


  • 第一步,將參數(shù)從右向左入棧(第1~6行)

  • 第二步,調(diào)用call 指令進(jìn)行跳轉(zhuǎn)(第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)部。

int max ( int num, ...) { 1. 00401020 push ebp 2. 00401021 mov ebp,esp 3. 00401023 sub esp,50h 4. 00401026 push ebx 5. 00401027 push esi 6. 00401028 push edi 7. 00401029 lea edi,[ebp-50h] 8. 0040102C mov ecx,14h 9. 00401031 mov eax,0CCCCCCCCh 10. 00401036 rep stos dword ptr [edi]
 va_list ap;
 int m = -0x7FFFFFFF; /* 32 系統(tǒng)中最小的整數(shù) */ 11. 00401038 mov dword ptr [ebp-8],80000001h
 va_start ( ap, num ); 12. 0040103F lea eax,[ebp+0Ch] 13. 00401042 mov dword ptr [ebp-4],eax
for ( int i= 0; i< num; i++ ) 14. 00401045 mov dword ptr [ebp-0Ch],0 15. 0040104C jmp max+37h (00401057) 16. 0040104E mov ecx,dword ptr [ebp-0Ch] 17. 00401051 add ecx,1 18. 00401054 mov dword ptr [ebp-0Ch],ecx 19. 00401057 mov edx,dword ptr [ebp-0Ch] 20. 0040105A cmp edx,dword ptr [ebp+8] 21. 0040105D jge max+61h (00401081) { int t= va_arg (ap, int); 22. 0040105F mov eax,dword ptr [ebp-4] 23. 00401062 add eax,4 24. 00401065 mov dword ptr [ebp-4],eax 25. 00401068 mov ecx,dword ptr [ebp-4] 26. 0040106B mov edx,dword ptr [ecx-4] 27. 0040106E mov dword ptr [t],edx if ( t > m ) 28. 00401071 mov eax,dword ptr [t] 29. 00401074 cmp eax,dword ptr [ebp-8] 30. 00401077 jle max+5Fh (0040107f)
 m = t; 31. 00401079 mov ecx,dword ptr [t] 32. 0040107C mov dword ptr [ebp-8],ecx
 } 33. 0040107F jmp max+2Eh (0040104e)
 va_end (ap); 34. 00401081 mov dword ptr [ebp-4],0
 return m; 35. 00401088 mov eax,dword ptr [ebp-8]
 } 36. 0040108B pop edi 37. 0040108C pop esi 38. 0040108D pop ebx 39. 0040108E mov esp,ebp 40. 00401090 pop ebp 41. 00401091 ret


  • 第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ù)指針,再在賦值指令里面回過頭來取先前的參數(shù)值賦給t(從mov edx,dword ptr [ecx-4]語句可以看出)。

  • 第36~41行恢復(fù)現(xiàn)場(chǎng)和堆棧地址,執(zhí)行函數(shù)返回操作。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉