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

以前只是知道可變參數(shù)怎么用,但是一直對(duì)它的原理是似懂非懂,現(xiàn)在對(duì)計(jì)算機(jī)有了比較深刻的認(rèn)識(shí)之后,回頭再看,豁然開朗。

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

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

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

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

于是我們可以自己寫可變參數(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;
}

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

    return 0;
}

測(cè)試結(jié)果如下:


可以看到,我們上面的實(shí)現(xiàn)有硬編碼的味道,也有沒有做字節(jié)對(duì)齊,為此系統(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í)際上只是一個(gè)參數(shù)指針,va_start根據(jù)你提供的最后一個(gè)固定參數(shù)來(lái)獲取第一個(gè)可變參數(shù)的地址,va_arg將指針指向下一個(gè)可變參數(shù)然后返回當(dāng)前值, va_end只是簡(jiǎn)單的將指針清0.

用下面的代碼進(jìn)行測(cè)試:
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ù)傳的是一個(gè)結(jié)構(gòu)體,結(jié)果會(huì)怎么樣呢?
下面的例子我們傳的可變參數(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;
}

運(yùn)行結(jié)果如下:


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

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

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

總的來(lái)說可變參數(shù)給我們提供了很高的靈活性和方便性,但是也給會(huì)造成不確定性,降低我們程序的安全性,很多時(shí)候可變參數(shù)數(shù)量或類型不匹配,就會(huì)造成一些不容察覺的問題,只有更好的理解它背后的原理,我們才能更好的駕馭它。
posted on 2012-09-18 00:04 Richard Wei 閱讀(1986) 評(píng)論(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>
            国产精品jizz在线观看美国| 久久精品主播| 久久久精品欧美丰满| 亚洲精品网站在线播放gif| 国产日韩一区在线| 欧美性猛交xxxx免费看久久久| 久久久xxx| 欧美综合激情网| 欧美一区二区在线看| 欧美一区二区视频免费观看 | 国产精品一级在线| 欧美日韩免费观看一区三区| 欧美极品在线观看| 欧美视频官网| 一区视频在线看| 亚洲精品四区| 久久精品国产精品亚洲综合| 免费欧美在线| 99热精品在线观看| 亚洲欧美日韩直播| 亚洲欧美日韩国产成人| 久久久久久国产精品mv| 欧美福利一区| 国产免费成人在线视频| 伊人春色精品| 亚洲在线免费| 亚洲国产精品久久久久久女王| 亚洲激情成人网| 久久精品免费看| 国产精品国产精品| 日韩视频永久免费| 久久人人97超碰国产公开结果| 欧美高清视频一二三区| 亚洲欧美日本国产有色| 国产精品ⅴa在线观看h| 亚洲最黄网站| 久久久av网站| 欧美成人乱码一区二区三区| 亚洲一区二区三区精品在线观看| 久久综合成人精品亚洲另类欧美| 国产精品盗摄久久久| 夜夜嗨av一区二区三区网页| 欧美激情在线狂野欧美精品| 亚洲一区二区成人在线观看| 欧美人在线视频| 日韩视频一区二区三区在线播放| 欧美福利视频| 欧美成人激情视频| 亚洲精品一区二区三区婷婷月| 久久亚洲精品伦理| 免费成人在线视频网站| 一区二区三区|亚洲午夜| 亚洲精品欧美激情| 国产精品久久久久毛片软件| 西瓜成人精品人成网站| 亚洲欧美视频| 亚洲欧洲精品一区二区| 亚洲精品乱码久久久久| 国产精品拍天天在线| 久久婷婷国产综合国色天香| 久久午夜av| 欧美一区二区三区四区夜夜大片| 亚洲免费视频网站| 亚洲二区在线视频| 亚洲特色特黄| 亚洲精选中文字幕| 欧美一区二区三区视频免费| 亚洲人成毛片在线播放女女| 在线亚洲国产精品网站| 极品日韩av| 一区二区三区国产在线| 亚洲自啪免费| 一区二区免费在线播放| 久久精品免费| 久久久午夜视频| 国产日本精品| 麻豆成人小视频| 亚洲小说欧美另类婷婷| 国产精品v日韩精品| 欧美成人午夜激情视频| 国产一区二区在线观看免费播放 | 亚洲精品日韩在线观看| 久久久999国产| 欧美激情乱人伦| 亚洲精品你懂的| 欧美人成在线视频| 亚洲午夜精品视频| 久久精品国产成人| 亚洲东热激情| 欧美日韩国产一区| 亚洲影院在线观看| 蜜桃久久精品一区二区| 99天天综合性| 国产综合香蕉五月婷在线| 午夜伦欧美伦电影理论片| 久久综合狠狠综合久久激情| 日韩亚洲欧美高清| 国产精品一二三四| 免费观看在线综合| 一区二区三区www| 久久深夜福利| 亚洲午夜小视频| 亚洲国产综合91精品麻豆| 国产精品成人免费精品自在线观看| 中文一区二区| 欧美大尺度在线| 欧美国产日本| 久久久999精品免费| 亚洲免费影视| 亚洲靠逼com| 91久久国产自产拍夜夜嗨| 午夜亚洲精品| 亚洲影院色无极综合| 国产精品激情| 欧美成人免费在线视频| 久久精品国产91精品亚洲| 一本久久综合亚洲鲁鲁| 亚洲人成绝费网站色www| 久久久免费精品| 久久综合久久综合久久综合| 亚洲自拍偷拍福利| 午夜国产欧美理论在线播放| 一区二区欧美国产| 亚洲在线网站| 亚洲一区影院| 午夜一区在线| 久久夜色撩人精品| 亚洲第一在线视频| 日韩视频在线一区二区三区| 一本色道久久综合亚洲精品小说| 亚洲精品国产精品国自产在线| 亚洲国产欧美在线| 亚洲精品中文字幕女同| 亚洲综合色婷婷| 快射av在线播放一区| 欧美日本二区| 亚洲人www| 夜夜精品视频| 欧美在线精品免播放器视频| 久久一区二区视频| 亚洲第一页中文字幕| 亚洲视频axxx| 欧美激情视频网站| 国产主播精品在线| 亚洲一区二区三区欧美| 欧美激情亚洲综合一区| 亚洲性感美女99在线| 小黄鸭精品密入口导航| 欧美国产日韩视频| 亚洲欧美综合v| 欧美日韩免费观看中文| 亚洲国产女人aaa毛片在线| 篠田优中文在线播放第一区| 亚洲精品美女| 欧美精品手机在线| 最新69国产成人精品视频免费| 久久久久国产一区二区三区四区 | 国产精品少妇自拍| 一区二区三区欧美在线| 亚洲人成免费| 欧美日本亚洲视频| 亚洲在线视频| 欧美一区二区三区男人的天堂| 久久久国产精品一区二区三区| 亚洲国产成人精品女人久久久| 久久久久国产精品人| 影院欧美亚洲| 亚洲日本精品国产第一区| 欧美久久久久中文字幕| 亚洲午夜羞羞片| 欧美亚洲综合另类| 红桃av永久久久| 欧美国产日本高清在线| 欧美激情视频网站| 小辣椒精品导航| 久久久97精品| 一本到12不卡视频在线dvd| 亚洲欧美日韩精品在线| 亚洲欧洲日韩在线| 国产精品99久久久久久久女警| 国产精品五区| 亚洲国产一区二区精品专区| 国产日韩在线亚洲字幕中文| 亚洲欧洲一区二区天堂久久| 国产视频亚洲精品| 亚洲美女精品久久| 亚洲肉体裸体xxxx137| 午夜精品久久久久久久99樱桃 | 亚洲影院色无极综合| 亚洲国产精品一区二区三区| 亚洲摸下面视频| 亚洲一区二区三区四区五区午夜 | 欧美国产成人精品| 美女久久网站| 韩曰欧美视频免费观看| 午夜精品久久久久久久99樱桃 | 亚洲欧美日韩一区二区在线| 国产精品久久久久久久久久直播| 欧美大片免费观看在线观看网站推荐| 国产精品高潮粉嫩av|