亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

可變參數函數的原理與實例

2019-11-14 09:01:59
字體:
來源:轉載
供稿:網友

在C/C++中,我們經常會用到可變參數的函數(比如PRintf/snprintf等),本篇筆記旨在講解編譯器借助va_start/va_arg/va_end這簇宏來實現可變參數函數的原理,并在文末給出簡單的實例。

        備注:本文的分析適用于linux/Windows,其它操作系統平臺的可變參數函數的實現原理大體相似。

1. 基礎知識        如果想要真正理解可變參數函數背后的運行機制,建議先理解兩部分基礎內容:         1)函數調用棧         2)函數調用約定        關于這兩個基礎知識點,我之前的筆記有詳細介紹,感興趣的童鞋可以移步這里:棧與函數調用慣例—上篇 和棧與函數調用慣例—下篇

2. 三個宏:va_start/va_arg/va_end        由man va_start可知,這簇宏定義在stdarg.h中,在我的測試機器上,該頭文件路徑為:/usr/lib/gcc/x86_64-redhat-Linux/3.4.5/include/stdarg.h,在gcc源碼中,其路徑為:gcc/include/stdarg.h。        在stdarg.h中,宏定義的相關代碼如下:

[cpp] view plain copy#define va_start(v,l)  __builtin_va_start(v,l)  #define va_end(v)      __builtin_va_end(v)  #define va_arg(v,l)    __builtin_va_arg(v,l)  #if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L  #define va_copy(d,s)    __builtin_va_copy(d,s)  #endif  #define __va_copy(d,s)  __builtin_va_copy(d,s)          其中,前3行就是我們所關心的va_start & var_arg & var_end的定義(至于va_copy,man中有所提及,但通常不會用到,想了解的同學可man查看之)??梢?,gcc將它們定義為一組builtin函數。        關于這組builtin函數的實現代碼,我曾試圖在gcc源碼中沿著調用路徑往下探索,無奈gcc為實現這組builtin函數引入了很多自定義的數據結構和宏,對非編譯器研究者的我來說,實在有點晦澀,最終探索過程無疾而終。在這里,我列出目前跟蹤到的調用路徑,以便有興趣的童鞋能繼續探索下去或指出我的不足,先在此謝過。        __builtin_va_start()函數的調用路徑:[cpp] view plain copy// file: gcc/builtins.c  /* The "standard" implementation of va_start: just assign `nextarg' to the variable.  */  void std_expand_builtin_va_start (tree valist, rtx nextarg)                          {                                                                                   rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE);      convert_move (va_r, nextarg, 0);  // definition is in gcc/expr.c  }  // 上述代碼中調用了expand_expr()來展開表達式,我猜測該函數調用完后,va_list指向了可變參數list前的最后一個已知類型參數  //  file: gcc/expr.h  /* Generate code for computing expression EXP.     An rtx for the computed value is returned.  The value is never null.     In the case of a void EXP, const0_rtx is returned.   */  static inline rtx expand_expr (tree exp, rtx target, enum machine_mode mode,enum expand_modifier modifier)  {     return expand_expr_real (exp, target, mode, modifier, NULL);  }  

3. Windows系統VS內置編譯器對va_start/va_arg/va_end的實現        如前所述,我沒能在gcc源碼中找出va_startva_arg/va_end這3個宏的實現代碼(⊙﹏⊙b汗),所幸的是,Windows平臺VS2008集成的編譯器中,對這三個函數有很明確的實現代碼,摘出如下。[cpp] view plain copy/* file path: Microsoft Visual Studio 9.0/VC/include/stdarg.h */  #include <vadefs.h>    #define va_start _crt_va_start  #define va_arg _crt_va_arg  #define va_end _crt_va_end          可見,Windows系統下,仍然將va_start/va_arg/va_end定義為一組宏。他們對應的實現在vadefs.h中:[cpp] view plain copy/* file path: Microsoft Visual Studio 9.0/VC/include/vadefs.h */  #ifdef  __cplusplus  #define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )  #else  #define _ADDRESSOF(v)   ( &(v) )  #endif    #define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )    #define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )  #define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )  #define _crt_va_end(ap)      ( ap = (va_list)0 )          備注:在VS2008提供的vadefs.h文件中,定義了若干組宏以支持不同的操作系統平臺,上面摘出的代碼片段是針對IA x86_32的實現。        下面對上面的代碼做個解釋:         a. 宏_ADDRESSOF(v)作用:取參數v的地址。         b. 宏_INTSIZEOF(n)作用:返回參數n的size并保證4字節對齊(32-bits平臺)。這個宏應用了一個小技巧來實現字節對齊:~(sizeof(int) - 1)的值對應的2進制值的低k位一定是0,其中sizeof(int) = 2^k,因此,在IA x86_32下,k=2。理解了這一點,那么(sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)的作用就很直觀了,它保證了sizeof(n)的值按sizeof(int)的值做對齊,例如在32-bits平臺下,就是按4字節對齊;在64-bits平臺下,按8字節對齊。至于為什么要保證對齊,與編譯器的底層實現有關,這里不再展開。         c. _crt_va_start(ap,v)作用:通過v的內存地址來計算ap的起始地址,其中,v是可變參數函數的參數中,最后一個類型已知的參數,執行的結果是ap指向可變參數列表的第1個參數。以int snprintf(char *str, size_t size, const char *format, ...)為例,其函數參數列表中最后一個已知類型的參數是const char *format,因此,參數format對應的就是_crt_va_start(ap, v)中的v, 而ap則指向傳入的第1個可變參數。        特別需要理解的是:為什么ap = address(v) + sizeof(v),這與函數棧從高地址向低地址的增長方向 及函數調用時參數從右向左的壓棧順序有關,這里默認大家已經搞清楚了這些基礎知識,不再展開詳述。         d. _crt_va_arg(ap,t)作用:更新指針ap后,取類型為t的變量的值并返回該值。         e. _crt_va_end(ap)作用:指針ap置0,防止野指針。        概括來說,可變參數函數的實現原理是:         1)根據函數參數列表中最后一個已知類型的參數地址,得到可變參數列表的第一個可變參數         2)根據程序員指定的每個可變參數的類型,通過地址及參數類型的size獲取該參數值         3)遍歷,直到訪問完所有的可變參數        從上面的實現過程可以注意到,可變參數的函數實現嚴重依賴于函數棧及函數調用約定(主要是參數壓棧順序),同時,依賴于程序員指定的可變參數類型。因此,若指定的參數類型與實際提供的參數類型不符時,程序出core簡直就是一定的。

4. 程序實例        經過上面對可變參數函數實現機制的分析,很容易實現一個帶可變參數的函數。程序實例如下:

[cpp] view plain copy#include <stdio.h>  #include <stdarg.h>    void foo(char *fmt, ...)   {      va_list ap;      int d;      char c, *p, *s;        va_start(ap, fmt);      while (*fmt)       {          if('%' == *fmt) {              switch(*(++fmt)) {                  case 's': /* string */                      s = va_arg(ap, char *);                      printf("%s", s);                      break;                  case 'd': /* int */                      d = va_arg(ap, int);                      printf("%d", d);                      break;                  case 'c': /* char */                      /* need a cast here since va_arg only takes fully promoted types */                      c = (char) va_arg(ap, int);                      printf("%c", c);                      break;                  default:                      c = *fmt;                      printf("%c", c);              }  // end of switch          }            else {              c = *fmt;              printf("%c", c);          }          ++fmt;      }      va_end(ap);  }    int main(int argc, char * argv[])  {      foo("sdccds%%, string=%s, int=%d, char=%c/n", "hello world", 211, 'k');      return 0;  }  

        上面的代碼很簡單,旨在拋磚引玉,只要真正搞清楚了可變參數函數的原理,相信各位會寫出更加復雜精細的可變參函數。         ^_^

【參考資料】1. linux man : va_start2. wikipedia - x86 calling conventions3. VS2008頭文件:stdarg.h和vadefs.h的源碼

================== EOF =================


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人中文字幕在线观看| 91视频国产高清| 91在线免费网站| 欧美日韩国产成人| 日韩美女av在线免费观看| 午夜剧场成人观在线视频免费观看| 国产精品电影网站| 久久免费视频在线观看| 中文字幕成人在线| 久久精品国产亚洲精品2020| 欧美午夜xxx| 国语自产精品视频在免费| 91成品人片a无限观看| 亚洲精品日韩激情在线电影| 亚洲免费中文字幕| 欧美一区深夜视频| 欧美猛交免费看| 精品久久久久久久久中文字幕| 日韩中文娱乐网| 国内精品久久久久伊人av| 日韩av在线免费播放| 亚洲高清色综合| 日韩电影在线观看永久视频免费网站| 久久精品99久久久香蕉| 日韩在线免费视频观看| 97国产精品免费视频| 日韩a**站在线观看| 国产精品自拍视频| 久久久精品免费| 国产免费一区二区三区香蕉精| 欧美疯狂xxxx大交乱88av| 亚洲欧美国产精品| 色婷婷亚洲mv天堂mv在影片| 午夜精品在线视频| 久久九九精品99国产精品| 久久天天躁狠狠躁夜夜躁2014| 啪一啪鲁一鲁2019在线视频| 精品少妇v888av| 国产成人精品在线观看| 久久久久久九九九| 久久精品国产一区二区电影| 自拍偷拍亚洲精品| 亚洲精品99999| 91精品国产91| 亚洲自拍高清视频网站| 91av在线视频观看| 日韩经典中文字幕在线观看| 国产免费一区二区三区在线观看| 亚洲精品视频网上网址在线观看| 亚洲女人被黑人巨大进入al| 亚洲电影免费观看| 成人h视频在线观看播放| 国产精品视频男人的天堂| 国产精品免费看久久久香蕉| 亚洲自拍偷拍色图| 国语自产精品视频在线看| 欧美日韩在线视频一区二区| 91po在线观看91精品国产性色| 欧美极品少妇xxxxⅹ裸体艺术| 亚洲精品资源美女情侣酒店| 精品国产一区二区三区久久久狼| 久久亚洲精品毛片| 精品亚洲一区二区三区在线播放| 92看片淫黄大片欧美看国产片| 97av在线播放| 日韩中文字幕视频在线观看| 日韩欧美国产黄色| 欧美成人免费在线视频| 亚洲欧美日韩在线一区| 91视频免费网站| 国产综合色香蕉精品| 日韩高清电影好看的电视剧电影| 一区二区日韩精品| 国产视频在线观看一区二区| 2019中文字幕在线观看| 久久精品一本久久99精品| 日韩中文字幕在线精品| 成人国产精品一区二区| 45www国产精品网站| 日韩在线视频网站| 亚洲日本欧美日韩高观看| 欧美亚洲免费电影| 久久99久久久久久久噜噜| 一区二区三区日韩在线| 亚洲成人国产精品| 欧美一区二区三区艳史| 国产精品极品美女粉嫩高清在线| 中文字幕亚洲一区二区三区五十路| 亚洲精品大尺度| 日韩va亚洲va欧洲va国产| 国产午夜精品美女视频明星a级| 中文字幕亚洲综合久久筱田步美| 97福利一区二区| 欧美一级在线亚洲天堂| 国产日韩专区在线| 欧美激情精品在线| 久久伊人精品天天| 波霸ol色综合久久| 欧美一区二区影院| 黑人巨大精品欧美一区二区免费| 91成人免费观看网站| 57pao成人国产永久免费| 亚洲黄色av网站| 欧美午夜久久久| 高清欧美性猛交xxxx黑人猛交| 国产精品久久久久久搜索| 国内免费精品永久在线视频| 亚洲三级黄色在线观看| 欧美色道久久88综合亚洲精品| 色婷婷久久av| 久久国产精品久久久久| 亚洲精品一区在线观看香蕉| 成人黄色生活片| 粉嫩av一区二区三区免费野| 97超碰蝌蚪网人人做人人爽| 欧美亚洲另类制服自拍| 久久久久国产视频| 亚洲视频第一页| 亚洲精品一区在线观看香蕉| 日韩高清欧美高清| 国产成人精品电影久久久| 午夜伦理精品一区| 亚洲视频日韩精品| 国产精品久久久久9999| 性欧美亚洲xxxx乳在线观看| 午夜精品久久久久久久男人的天堂| 最近免费中文字幕视频2019| 亚洲欧洲高清在线| 亚洲精品资源在线| 九九精品在线播放| 中文字幕日韩欧美在线视频| 尤物yw午夜国产精品视频明星| 欧美日韩国产一区二区三区| 57pao国产成人免费| 亚洲第一中文字幕在线观看| 亚洲精选一区二区| 欧美风情在线观看| 日韩欧美中文字幕在线播放| 九九精品在线播放| 91久久在线视频| 国产精品久久久久影院日本| 亚洲国产成人精品一区二区| 精品国产一区二区三区久久久狼| 68精品久久久久久欧美| 亚洲电影免费观看高清完整版在线观看| 久久伊人免费视频| 国产精品白嫩初高中害羞小美女| 欧美另类交人妖| 国产成人鲁鲁免费视频a| 欧美激情国产日韩精品一区18| 久久中文精品视频| 中文一区二区视频| 国产精品视频一区二区高潮| 在线播放日韩精品| 日韩成人中文字幕| 亚洲深夜福利网站| 久久久久久亚洲精品中文字幕| 精品久久中文字幕久久av| 欧美精品国产精品日韩精品| 久久成人亚洲精品| 日韩欧美aⅴ综合网站发布| 国产suv精品一区二区三区88区| 国产精品大片wwwwww| 成人妇女免费播放久久久|