<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      厚積薄發(fā)
      海納百川,有容乃大
      以前只是知道可變參數(shù)怎么用,但是一直對它的原理是似懂非懂,現(xiàn)在對計算機有了比較深刻的認識之后,回頭再看,豁然開朗。

      要理解可變參數(shù),首先要理解函數(shù)調(diào)用約定, 為什么只有__cdecl的調(diào)用約定支持可變參數(shù),而__stdcall就不支持?

      實際上__cdecl和__stdcall函數(shù)參數(shù)都是從右到左入棧,它們的區(qū)別在于由誰來清棧,__cdecl由外部調(diào)用函數(shù)清棧,而__stdcall由被調(diào)用函數(shù)本身清棧, 顯然對于可變參數(shù)的函數(shù),函數(shù)本身沒法知道外部函數(shù)調(diào)用它時傳了多少參數(shù),所以沒法支持被調(diào)用函數(shù)本身清棧(__stdcall), 所以可變參數(shù)只能用__cdecll.

      另外還要理解函數(shù)參數(shù)傳遞過程中堆棧是如何生長和變化的,從堆棧低地址到高地址,依次存儲 被調(diào)用函數(shù)局部變量,上一函數(shù)堆棧楨基址,函數(shù)返回地址,參數(shù)1, 參數(shù)2, 參數(shù)3...,相關知識可以參考我的這篇堆棧楨的生成原理 

      有了上面的知識,我可以知道函數(shù)調(diào)用時,參數(shù)2的地址就是參數(shù)1的地址加上參數(shù)1的長度,而參數(shù)3的地址是參數(shù)2的地址加上參數(shù)2的長度,以此類推。

      于是我們可以自己寫可變參數(shù)的函數(shù)了, 代碼如下:
      int Sum(int nCount, )
      {
          int nSum = 0;
          int* p = &nCount;
          for(int i=0; i<nCount; ++i)
          {
              cout << *(++p) << endl;
              nSum += *p;
          }

          cout << "Sum:" << nSum << endl << endl;
          return nSum;
      }

      string  SumStr(int nCount, )
      {
          string str;
          int* p = &nCount;

          for(int i=0; i<nCount; ++i)
          {
              char* pTemp = (char*)*(++p);
              cout <<  pTemp << endl;
              str += pTemp;
          }

          cout << "SumStr:" << str << endl;
          return str;
      }

      在我們的測試函數(shù)中nCount表示后面可變參數(shù)的個數(shù),int Sum(int nCount, )會打印后面的可變參數(shù)Int值,并且進行累加;string  SumStr(int nCount, ) 會打印后面可變參數(shù)字符串內(nèi)容,并連接所有字符串。
      然后用下面代碼進行測試:int main() 
      {
          Sum(3, 10, 20, 30);
          SumStr(5, "aa", "bb", "cc", "dd", "ff");
          
          system("pause");

          return 0;
      }

      測試結(jié)果如下:


      可以看到,我們上面的實現(xiàn)有硬編碼的味道,也有沒有做字節(jié)對齊,為此系統(tǒng)專門給我們封裝了一些支持可變參數(shù)的宏:
      //typedef char *  va_list;

      //#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
      //#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 )

      //#define va_start _crt_va_start
      //#define va_arg _crt_va_arg
      //#define va_end _crt_va_end

      用系統(tǒng)的這些宏,我們的代碼可以這樣寫了:
      //use va_arg, praram is int
      int SumNew(int nCount, )
      {
          int nSum = 0;
          va_list vl = 0;
          va_start(vl, nCount);

          for(int i=0; i<nCount; ++i)
          {
              int n = va_arg(vl, int);
              cout << n << endl;
              nSum += n;
          }

          va_end(vl);
          cout << "SumNew:" << nSum << endl << endl;
          return nSum;
      }

      //use va_arg,  praram is char*
      string SumStrNew(int nCount, )
      {
          string str;
          va_list vl = 0;
          va_start(vl, nCount);

          for(int i=0; i<nCount; ++i)
          {
              char* p = va_arg(vl, char*);
              cout <<  p << endl;
              str += p;
          }

          cout << "SumStrNew:" << str << endl << endl;
          return str;
      }

      可以看到,其中 va_list實際上只是一個參數(shù)指針,va_start根據(jù)你提供的最后一個固定參數(shù)來獲取第一個可變參數(shù)的地址,va_arg將指針指向下一個可變參數(shù)然后返回當前值, va_end只是簡單的將指針清0.

      用下面的代碼進行測試:
      int main() 
      {
          Sum(3, 10, 20, 30);
          SumStr(5, "aa", "bb", "cc", "dd", "ff");
          
          SumNew(3, 1, 2, 3);
          SumStrNew(3, "12", "34", "56");

          system("pause");

          return 0;
      }

      結(jié)果如下: 


      我們上面的例子傳的可變參數(shù)都是4字節(jié)的, 如果我們的可變參數(shù)傳的是一個結(jié)構(gòu)體,結(jié)果會怎么樣呢?
      下面的例子我們傳的可變參數(shù)是std::string
      //use va_arg,  praram is std::string
      void SumStdString(int nCount, )
      {
          string str;
          va_list vl = 0;
          va_start(vl, nCount);

          for(int i=0; i<nCount; ++i)
          {
              string p = va_arg(vl, string);
              cout <<  p << endl;
              str += p;
          }

          cout << "SumStdString:" << str << endl << endl;
      }

      int main() 
      {
      Sum(3, 10, 20, 30);
      SumStr(5, "aa", "bb", "cc", "dd", "ff");
      SumNew(3, 1, 2, 3);
      SumStrNew(3, "12", "34", "56");
      string s1("hello ");
      string s2("world ");
      string s3("!");
      SumStdString(3, s1, s2, s3);
      system("pause");
      return 0;
      }

      運行結(jié)果如下:


      可以看到即使傳入的可變參數(shù)是std::string, 依然可以正常工作。
      我們可以反匯編下看看這種情況下的參數(shù)傳遞過程:

      很多時候編譯器在傳遞類對象時,即使是傳值,也會在堆棧上通過push對象地址的方式來傳遞,但是上面顯然沒有這么做,因為它要滿足可變參數(shù)堆棧內(nèi)存連續(xù)分布的規(guī)則, 另外,可以看到最后在調(diào)用sumStdString后,由add esp, 58h來外部清棧。
      一個std::string大小是28, 58h = 88 = 28 + 28 + 28 + 4.

      從上面的例子我們可以看到,對于可變參數(shù)的函數(shù),有2種東西需要確定,一是可變參數(shù)的數(shù)量, 二是可變參數(shù)的類型,上面的例子中,參數(shù)數(shù)量我們是在第一個參數(shù)指定的,參數(shù)類型我們是自己約定的。這種方式在實際使用中顯然是不方便,于是我們就有了_vsprintf, 我們根據(jù)一個格式化字符串的來表示可變參數(shù)的類型和數(shù)量,比如C教程中入門就要學習printf, sprintf等。

      總的來說可變參數(shù)給我們提供了很高的靈活性和方便性,但是也給會造成不確定性,降低我們程序的安全性,很多時候可變參數(shù)數(shù)量或類型不匹配,就會造成一些不容察覺的問題,只有更好的理解它背后的原理,我們才能更好的駕馭它。
      posted on 2012-09-18 00:29  Richard Wei  閱讀(9965)  評論(1)    收藏  舉報

      主站蜘蛛池模板: 久久久一本精品99久久精品36| 中国性欧美videofree精品| 亚洲综合小综合中文字幕| 重口SM一区二区三区视频| 亚洲午夜福利精品无码不卡| 午夜福利日本一区二区无码| 日本一区二区久久人妻高清| 欧美怡春院一区二区三区| 日本中文字幕有码在线视频 | 一本大道久久香蕉成人网| a4yy私人毛片| 丁香花在线观看免费观看图片| 亚洲一区二区三区激情在线| 青青青爽在线视频观看| 午夜福利片1000无码免费| 亚洲色成人网站www永久四虎| 欧美老少配性行为| 粗壮挺进人妻水蜜桃成熟 | 亚洲欧美人成人让影院| 精品午夜福利在线视在亚洲| 成人国产乱对白在线观看 | 天天色综网| 亚洲综合色一区二区三区| 妺妺窝人体色www婷婷| 农村老熟女一区二区三区| 久久综合久中文字幕青草| 国产午夜福利精品视频| 精品久久人人妻人人做精品| 欧美区一区二区三区| 日韩精品一二三黄色一级| 久久精品人成免费| 国产剧情福利一区二区麻豆 | 亚洲精品香蕉一区二区| 99精品国产兔费观看久久99| 无码内射中文字幕岛国片| 77se77亚洲欧美在线| 亚洲电影在线观看| 精品亚洲一区二区三区四区| 亚洲鸥美日韩精品久久| 丰满的少妇被猛烈进入白浆| 亚洲日本中文字幕乱码中文|