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

posts - 311, comments - 0, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

(搬運工)C/C++中可變參數的原理

Posted on 2012-09-18 14:27 點點滴滴 閱讀(224) 評論(0)  編輯 收藏 引用 所屬分類: 02 編程語言
以前只是知道可變參數怎么用,但是一直對它的原理是似懂非懂,現在對計算機有了比較深刻的認識之后,回頭再看,豁然開朗。

要理解可變參數,首先要理解函數調用約定, 為什么只有__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等。

總的來說可變參數給我們提供了很高的靈活性和方便性,但是也給會造成不確定性,降低我們程序的安全性,很多時候可變參數數量或類型不匹配,就會造成一些不容察覺的問題,只有更好的理解它背后的原理,我們才能更好的駕馭它。
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            免费成人av在线看| 欧美在线免费观看亚洲| 欧美日韩中文另类| 欧美成人国产一区二区| 女女同性女同一区二区三区91| 久久中文字幕一区| 美女精品在线| 欧美视频你懂的| 国产欧美日韩亚洲| 国产偷自视频区视频一区二区| 尤物精品国产第一福利三区 | 国产精品久久久久久久9999| 亚洲先锋成人| 亚洲欧美成aⅴ人在线观看| 亚洲国产福利在线| 99re这里只有精品6| 亚洲欧美卡通另类91av| 久久三级视频| 亚洲精品乱码久久久久久按摩观| 欧美福利视频在线| 日韩图片一区| 欧美一二区视频| 欧美区一区二区三区| 欧美性大战久久久久| 国产揄拍国内精品对白| 日韩亚洲一区二区| 欧美一区二区国产| 欧美国产精品中文字幕| 亚洲一级片在线看| 老司机午夜精品视频| 国产精品久久久久久久久久久久| 亚洲第一精品夜夜躁人人躁| 亚洲欧美日韩另类| 最近中文字幕mv在线一区二区三区四区 | 亚洲国产你懂的| 欧美一区二区三区在线免费观看| 亚洲成人在线网| 欧美中文日韩| 国产精品免费在线| 亚洲美女免费视频| 免费在线播放第一区高清av| 午夜精品久久| 国产精品亚洲美女av网站| 亚洲精选中文字幕| 免费欧美日韩| 欧美在线国产| 国产精品爽黄69| 亚洲在线观看视频网站| 亚洲欧洲精品一区二区三区波多野1战4 | 99在线观看免费视频精品观看| 久久久久久久久久码影片| 一本大道久久a久久精品综合| 猛干欧美女孩| 在线日韩欧美视频| 久久福利毛片| 亚洲影院免费| 国产精品自在线| 亚洲一二三区视频在线观看| 欧美成人dvd在线视频| 久久久久久噜噜噜久久久精品| 国产一区二区欧美日韩| 久久精品人人| 久久国产精品色婷婷| 国产乱码精品一区二区三区忘忧草| 亚洲一区二区免费看| 久久频这里精品99香蕉| 国产精品久久久99| 亚洲少妇自拍| 欧美一区二视频在线免费观看| 黑人巨大精品欧美一区二区 | 久久只有精品| 久久婷婷国产综合尤物精品| 亚洲人人精品| 欧美国产日本韩| 亚洲精品中文字幕有码专区| 亚洲国产综合视频在线观看| 欧美精品一区二区三| 在线视频亚洲| 亚洲一区中文| 黄色精品一区| 亚洲高清不卡av| 欧美日韩亚洲一区二区三区在线观看| 亚洲视频综合| 性色av一区二区三区在线观看| 国产综合一区二区| 亚洲国产二区| 国产精品国产自产拍高清av王其| 午夜久久电影网| 久久久久久自在自线| 亚洲精品乱码久久久久久黑人| 亚洲精品在线免费| 国产精品福利在线| 久久一区精品| 欧美成人黑人xx视频免费观看| av不卡在线| 午夜精品在线观看| 亚洲人www| 亚洲私拍自拍| 亚洲级视频在线观看免费1级| a4yy欧美一区二区三区| 韩国av一区二区三区| 最近看过的日韩成人| 韩国av一区二区三区四区| 亚洲国内高清视频| 国产欧美精品日韩精品| 亚洲国产mv| 国产麻豆日韩欧美久久| 亚洲破处大片| 在线观看91精品国产麻豆| 在线视频亚洲欧美| 亚洲欧洲另类| 久久精品国产清自在天天线| 亚洲一区二区精品在线观看| 免费看的黄色欧美网站| 久久成人18免费网站| 欧美人体xx| 久久综合影视| 国产精品日产欧美久久久久| 亚洲成人自拍视频| 国产一区二区精品久久| 一区二区不卡在线视频 午夜欧美不卡'| 国产欧美日韩精品在线| 欧美一区在线看| 欧美激情亚洲精品| 欧美大片在线影院| 国语自产偷拍精品视频偷| 亚洲欧美另类中文字幕| 亚洲天堂av在线免费| 欧美一区二区三区在线免费观看| 亚洲美女视频在线观看| 亚洲人成人一区二区三区| 久久久久久久成人| 欧美一区二区视频97| 国产欧美日本一区视频| 亚洲校园激情| 欧美一区二区| 国产免费成人av| 亚洲欧美一区二区三区极速播放 | 欧美一区二区三区四区在线| 国产精品无码专区在线观看 | 亚洲综合999| 欧美一区二区三区在线| 国产日韩一区二区三区在线播放| 亚洲影院污污.| 欧美在线影院| 国产小视频国产精品| 欧美在线视频一区二区| 久久综合久久88| 亚洲国产天堂网精品网站| 免费在线一区二区| 亚洲精品视频免费观看| 中文网丁香综合网| 国产精品乱码久久久久久| 亚洲综合999| 久久一区中文字幕| 亚洲国产小视频| 欧美日韩一区自拍| 午夜精品网站| 亚洲国产日韩在线| 亚洲欧美久久| 一区二区在线视频| 欧美激情一区二区三区在线视频观看 | 一区二区三区偷拍| 欧美一级午夜免费电影| 国内精品国产成人| 免费日韩av片| aⅴ色国产欧美| 久久―日本道色综合久久| 亚洲欧洲中文日韩久久av乱码| 欧美日韩国产美| 香蕉久久夜色精品| 欧美激情一区二区三区全黄| 亚洲欧美日韩在线播放| 在线精品观看| 国产精品日韩精品欧美精品| 看欧美日韩国产| 日韩性生活视频| 欧美本精品男人aⅴ天堂| 亚洲综合色自拍一区| 亚洲国产精品成人综合色在线婷婷| 欧美三级网址| 欧美一区二区啪啪| 久久久久九九九九| 亚洲美女电影在线| 午夜精品福利电影| 亚洲欧洲日本在线| 国产在线精品二区| 欧美日韩一区二区三区在线 | 欧美制服第一页| 一本一本a久久| 亚洲国产高清aⅴ视频| 久热国产精品视频| 午夜国产不卡在线观看视频| 亚洲国产一区二区视频| 国产亚洲精品一区二区| 欧美人与性动交a欧美精品| 久久人人爽人人爽爽久久| 午夜欧美大尺度福利影院在线看| 亚洲免费福利视频| 亚洲日本aⅴ片在线观看香蕉|