青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

以前只是知道可變參數怎么用,但是一直對它的原理是似懂非懂,現在對計算機有了比較深刻的認識之后,回頭再看,豁然開朗。

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

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

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

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

于是我們可以自己寫可變參數的函數了, 代碼如下:
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;
}

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

    return 0;
}

測試結果如下:


可以看到,我們上面的實現有硬編碼的味道,也有沒有做字節對齊,為此系統專門給我們封裝了一些支持可變參數的宏:
//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

用系統的這些宏,我們的代碼可以這樣寫了:
//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實際上只是一個參數指針,va_start根據你提供的最后一個固定參數來獲取第一個可變參數的地址,va_arg將指針指向下一個可變參數然后返回當前值, 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;
}

結果如下: 


我們上面的例子傳的可變參數都是4字節的, 如果我們的可變參數傳的是一個結構體,結果會怎么樣呢?
下面的例子我們傳的可變參數是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;
}

運行結果如下:


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

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

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

總的來說可變參數給我們提供了很高的靈活性和方便性,但是也給會造成不確定性,降低我們程序的安全性,很多時候可變參數數量或類型不匹配,就會造成一些不容察覺的問題,只有更好的理解它背后的原理,我們才能更好的駕馭它。
posted on 2012-09-18 00:04 Richard Wei 閱讀(1990) 評論(0)  編輯 收藏 引用 所屬分類: C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美欧美天天天天操| 亚洲新中文字幕| 亚洲一区免费观看| 99精品国产在热久久下载| 亚洲国产小视频| 亚洲国产成人久久综合一区| 亚洲电影观看| 亚洲精品麻豆| 一区二区三区视频在线| 亚洲美女在线国产| 亚洲男人第一网站| 欧美制服丝袜第一页| 久久影院午夜片一区| 欧美高清视频一二三区| 午夜免费久久久久| 亚洲高清视频一区| 一本色道精品久久一区二区三区| 日韩亚洲不卡在线| 在线亚洲观看| 久久精品国产清高在天天线| 狂野欧美激情性xxxx| 亚洲人成在线观看一区二区| 亚洲区一区二| 欧美一级黄色网| 欧美gay视频| 国产欧美日韩一区二区三区在线观看| 国产综合色在线| 一本色道久久综合亚洲精品婷婷| 午夜精品一区二区三区四区| 久久天天狠狠| 亚洲乱码日产精品bd| 欧美一区激情视频在线观看| 欧美国产视频在线| 国内成人自拍视频| 一本色道久久综合| 久久天天狠狠| 一区二区三区欧美成人| 另类av一区二区| 国产日韩在线视频| 亚洲调教视频在线观看| 狼狼综合久久久久综合网| 一本久久青青| 欧美激情在线免费观看| 国产一区二区三区四区三区四 | 欧美一区二区三区视频在线| 美脚丝袜一区二区三区在线观看| 欧美日韩在线第一页| 蜜臀久久久99精品久久久久久 | 亚洲第一区在线观看| 午夜精品视频| 国产精品免费看久久久香蕉| 亚洲免费观看高清完整版在线观看熊 | 亚洲电影在线看| 久久精品综合一区| 亚洲一级二级| 国产精品国产馆在线真实露脸| 亚洲激情午夜| 欧美国产成人精品| 久久综合一区| 在线观看亚洲| 久久在线免费观看| 欧美在线观看视频一区二区三区| 国产精品手机视频| 午夜在线成人av| 亚洲天堂成人| 欧美第一黄色网| 欧美专区18| 国内外成人在线| 久久久免费av| 亚洲欧洲精品一区| 欧美成人精品一区二区三区| 亚洲大片免费看| 欧美激情按摩在线| 欧美成人综合一区| 在线中文字幕不卡| 亚洲视频免费观看| 国产啪精品视频| 久久嫩草精品久久久久| 久久久99爱| 亚洲国产视频直播| 91久久精品国产91久久性色| 欧美日韩国产小视频在线观看| 一本色道久久综合| 亚洲一区二区三区四区在线观看| 国产精品亚洲综合天堂夜夜| 久久狠狠亚洲综合| 老牛影视一区二区三区| 亚洲精品乱码久久久久久蜜桃麻豆| 亚洲欧洲中文日韩久久av乱码| 欧美区一区二| 欧美一区免费视频| 久久久久在线观看| 一区二区三区四区国产精品| 亚洲午夜精品久久| 国产一区二区无遮挡| 欧美成人综合一区| 欧美三日本三级少妇三2023| 欧美一区二区三区久久精品| 久久成人18免费观看| 亚洲经典三级| 一区二区电影免费观看| 国产一区亚洲| 夜夜嗨av一区二区三区免费区| 国产日产欧美精品| 亚洲欧洲日本国产| 精品动漫3d一区二区三区免费| 亚洲国产日韩欧美综合久久| 国产精品麻豆欧美日韩ww| 久热国产精品视频| 国产精品劲爆视频| 欧美激情影院| 国产亚洲一区二区三区| 欧美激情视频给我| 国产综合激情| 99国产精品久久久久久久| 精品动漫3d一区二区三区| 99精品视频免费全部在线| 在线观看成人网| 亚洲在线1234| 亚洲性夜色噜噜噜7777| 可以看av的网站久久看| 欧美一区精品| 国产精品视频精品| 亚洲美女在线一区| 亚洲精品三级| 久久综合伊人77777蜜臀| 欧美专区第一页| 香蕉久久一区二区不卡无毒影院| 亚洲激情视频在线| 西瓜成人精品人成网站| 亚洲一区二区三区乱码aⅴ蜜桃女| 麻豆九一精品爱看视频在线观看免费 | 亚洲欧洲精品天堂一级| 欧美在线免费观看视频| 亚洲欧美日韩一区二区| 欧美日韩免费观看一区| 亚洲激情影院| 亚洲三级免费观看| 欧美va天堂在线| 欧美www视频| 影音先锋日韩有码| 久久精品国产91精品亚洲| 久久精品国产99国产精品澳门| 国产精品家教| 亚洲自拍电影| 欧美专区在线观看| 国产专区精品视频| 欧美在线视频二区| 久久亚洲一区二区| 亚洲高清不卡av| 欧美成人精品在线观看| 亚洲黄色在线看| av成人国产| 欧美视频一区二区三区四区| 夜夜爽av福利精品导航| 亚洲主播在线播放| 国产精品系列在线播放| 欧美一级电影久久| 另类天堂av| 亚洲欧洲日韩在线| 欧美日韩另类视频| 午夜伦理片一区| 久久综合亚州| 亚洲精品乱码久久久久久日本蜜臀 | 一区二区三区在线看| 麻豆国产精品777777在线| 亚洲国产成人久久综合| 亚洲视频免费看| 国产一区二区三区在线观看视频| 久久亚洲春色中文字幕久久久| 亚洲电影av| 亚洲在线播放| 狠狠入ady亚洲精品| 欧美精品一区二区蜜臀亚洲| 一区二区三区日韩欧美| 久久偷看各类wc女厕嘘嘘偷窃| 亚洲国产福利在线| 国产精品免费观看视频| 老司机精品福利视频| 中文在线资源观看网站视频免费不卡| 久久亚洲欧美| 亚洲自拍高清| 亚洲国产精品国自产拍av秋霞 | 欧美/亚洲一区| 激情成人av| 欧美一二区视频| 亚洲视频狠狠| 欧美色播在线播放| 亚洲天天影视| 久久久久国产一区二区三区四区| 最新精品在线| 欧美日韩精品免费| 亚洲一区在线播放| 亚洲激情欧美| 国产精品永久免费视频| 久久精品二区亚洲w码| 亚洲国产成人精品久久| 久久精品一区中文字幕| 亚洲欧洲日产国产综合网| 欧美久色视频|