欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++可變參數(shù)的實(shí)現(xiàn)方法

 更新時(shí)間:2013年03月13日 22:53:49   作者:  
可變參數(shù)給編程帶來(lái)了很大的方便,在享受它帶來(lái)的方便的同時(shí),很有必要了解一下其實(shí)現(xiàn)方式,在了解編程語(yǔ)言的同時(shí),也可以擴(kuò)展編程的思路。

可變參數(shù)的實(shí)現(xiàn)要解決三個(gè)問(wèn)題:

1.如何調(diào)用帶有可變參數(shù)的函數(shù)
2.如何編譯有可變參數(shù)的程序
3.在帶有可變參數(shù)的函數(shù)體中如何持有可變參數(shù)
第一個(gè)問(wèn)題, 調(diào)用時(shí)在可以傳入可變參數(shù)的地方傳入可變參數(shù)即可,當(dāng)然,還有一些需要注意的地方,后面會(huì)提到。

第二個(gè)問(wèn)題,編譯器需要在編譯時(shí)采用一種寬松的檢查方案,,這會(huì)帶來(lái)一些問(wèn)題, 比如對(duì)編程查錯(cuò)不利。

第三個(gè)是我在這里要關(guān)心的問(wèn)題,先以C語(yǔ)言為例分析其實(shí)現(xiàn)原理。

printf和scanf是C語(yǔ)言標(biāo)準(zhǔn)庫(kù)中最常見(jiàn)的可變參數(shù)函數(shù), printf的簽名是

復(fù)制代碼 代碼如下:

int printf(const char* format, ...);

其中,... 表示可變參數(shù),現(xiàn)在模仿printf寫(xiě)一個(gè)簡(jiǎn)單的例子。

一、一個(gè)簡(jiǎn)單了例子:

復(fù)制代碼 代碼如下:

#include <windows.h>
#include <stdio.h>

void VariableArgumentMethod(int argc, ...);

int main(){
    VariableArgumentMethod(6, 4, 7, 3, 0, 7, 9);
    return 0;
}

void VariableArgumentMethod(int argc, ...){
    // 聲明一個(gè)指針, 用于持有可變參數(shù)
    va_list pArg;
    // 將 pArg 初始化為指向第一個(gè)參數(shù)
    va_start(pArg, argc);
    // 輸出參數(shù)
  for(int i = 0; i != argc; ++i){
        // 獲取 pArg 所指向的參數(shù)并輸出
        printf("%d, ", va_arg(pArg, int) );
    }

    va_end(pArg);
}


void VariableArgumentMethod(int argc, ...)是一個(gè)可變參數(shù)函數(shù),這個(gè)函數(shù)用于將 argc 指定個(gè)數(shù)的可變參數(shù)輸出。
VariableArgumentMethod(6, 4, 7, 3, 0, 7, 9); 是對(duì)這個(gè)函數(shù)的調(diào)用,第一個(gè)實(shí)參 6 表示后面跟了 6 個(gè)參數(shù)。

在 VariableArgumentMethod 的函數(shù)體中:

1. va_list pArg;

定義了一個(gè)用于持有可變參數(shù)的指針,通過(guò)將這個(gè)指針在傳入的可變參數(shù)表中移動(dòng),可以持有第一個(gè)可變參數(shù)。

2. va_start(pArg, argc);

讓 pArg 指向可變參數(shù)列表中的第一個(gè)參數(shù)。argc 是一個(gè)用來(lái)定位的參數(shù),因?yàn)榭勺儏?shù)是從 argc 后開(kāi)始的,后面會(huì)說(shuō)明為什么要這樣定位。

3. va_arg(pArg, int);

這句話放在循環(huán)體中,用于取出可變參數(shù)表中的參數(shù)。并且,它會(huì)讓 pArg 移向下個(gè)可變參數(shù)(如果已經(jīng)到達(dá)末尾,則它將指向一個(gè)沒(méi)有意義的地址)。

4. va_end(pArg);

給 pArg 清零,個(gè)人認(rèn)為在這里可有可無(wú),因?yàn)?pArg 已經(jīng)不需要了。

 

就這樣,VariableArgumentMethod 函數(shù)體遍歷了可變參數(shù)表中傳入的參數(shù),并用printf("%d, ", va_arg(pArg, int) ) 進(jìn)行了輸出。

二、實(shí)現(xiàn)細(xì)節(jié)

1. 先了解一下編譯器如何處理傳遞參數(shù)這個(gè)問(wèn)題的。

編譯器是將參數(shù)壓入棧中進(jìn)行傳遞的。傳遞實(shí)參的時(shí)候,編譯器會(huì)從實(shí)參列表中,按從右到左的順序?qū)?shù)入棧,對(duì)于 VariableArgumentMethod(6, 4, 7, 3, 0, 7, 9)調(diào)用,則入棧的順序是 9, 7, 0, 3, 7, 4, 6 (注意沒(méi)有可變參數(shù)與不可變參數(shù)之分)。由于棧的地址是從高到低的,所以實(shí)參入棧后,實(shí)參在棧中的分布如下圖??梢钥闯觯瑢?shí)參在棧中,還是保持了左邊參數(shù)處于低地址,右邊參數(shù)處于高地址的狀態(tài)。OK,知道這些就夠了。

低地址                            高地址

...

6

4

7

3

0

7

9

...

 

2. va_list, va_start, va_arg 和 va_end

va_list 是一個(gè)定義的指針類(lèi)型,va_start, va_arg 和 va_end 都是C語(yǔ)言用于處理可變參數(shù)而定義的宏,在stdarg.h文件中。由于硬件平臺(tái)的不同,編譯器的不同,導(dǎo)致它們的定義也有所不同,但基本思路相同。以下是相關(guān)宏的定義。

復(fù)制代碼 代碼如下:

typedef char *  va_list;

#define _ADDRESSOF(v)   ( &(v) )

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

可以看出,此處引入了另外兩個(gè)宏 _ADDRESSOF 和 _INTSIZEOF。

_ADDRESSOF(v) 是用于獲取變量地址的,這一眼就能看出來(lái);

_INTSIZEOF(n) 是用于對(duì)齊的。(什么是對(duì)齊呢?這是因?yàn)闂5慕Y(jié)構(gòu)導(dǎo)致的,在 32 位機(jī)中,棧中每個(gè)單元都是占 4 個(gè)字節(jié)的,這往往是一個(gè) int 型的長(zhǎng)度,但實(shí)際傳過(guò)來(lái)的參數(shù)可能并不正好是 4 個(gè)字節(jié),或者正好是 4 的倍數(shù)個(gè)字節(jié),就好像坐車(chē)時(shí)不會(huì)賣(mài)半個(gè)座位給乘客一樣,如果傳入的數(shù)據(jù)沒(méi)有正好占 4 個(gè)或 4 的倍數(shù)個(gè)字節(jié),則需要對(duì)齊(補(bǔ)齊)。至于為什么這個(gè)表達(dá)式能夠?qū)R,需要分析一下);

va_start(ap,v) 中,ap 是用于持有可變參數(shù)的指針, v 是最后一個(gè)非可變參數(shù)的參數(shù),(va_list)_ADDRESSOF(v) 獲取 v 的地址,并轉(zhuǎn)為 va_list 類(lèi)型的,v 是最后一個(gè)非可變參數(shù)的參數(shù),在本例中應(yīng)為 6, 在上圖中處理?xiàng)5牡偷刂范耍琠INTSIZEOF(v) 獲取了一個(gè)對(duì)齊地址,這里應(yīng)為 4, 兩個(gè)相加后,即指向了第一個(gè)可變參數(shù),即上圖中的 4, 將這個(gè)值賦給 ap 后,就讓 ap 指向了第一個(gè)可變參數(shù)。(從這里可以看出,將va_list 定義為 char* 是很有用的,因?yàn)?char 長(zhǎng)度為一個(gè)字節(jié),便于指針運(yùn)算);

 va_arg(ap,t) 中,ap 是用于持有可變參數(shù)的指針,t 是要獲取參數(shù)的類(lèi)型,ap += _INTSIZEOF(t) 讓 ap 指向下一個(gè)參數(shù),但是,此處還需要獲取當(dāng)前參數(shù)的值,所以又將表達(dá)式減回來(lái),返回的應(yīng)是一個(gè) va_list(char*) 型的指針,因此要轉(zhuǎn)型為 t* 后再進(jìn)行解引用運(yùn)算,得到當(dāng)前參數(shù)的值。(注意這里有個(gè)將 ap 移向下一個(gè)參數(shù)又減回來(lái)的操作,本人感覺(jué)不太好,一方面這里有個(gè)浪費(fèi)的操作,對(duì)性能會(huì)有一些影響,另一方面,我更希望將取當(dāng)前值的操作和移向下一個(gè)的操作分離,這樣可以讓程序員有更多的控制,并且容易理解。)

va_end(ap) 則是讓 ap 指向一個(gè)空地址。

通過(guò)以上分析,可以發(fā)現(xiàn),C 語(yǔ)言中可變參數(shù)是從棧中按順序訪問(wèn)的,過(guò)程中所使用的三個(gè)宏,也只是對(duì)操作的簡(jiǎn)單包裝,完全可以自己編程實(shí)現(xiàn)。而且,參數(shù)的類(lèi)型和個(gè)數(shù)是不能直接確定的,在本例中,VariableArgumentMethod 的第一個(gè)參數(shù)用于指定參數(shù)的個(gè)數(shù),而參數(shù)的類(lèi)型約定為整形,這樣程序才能正常運(yùn)行,再說(shuō)到 printf,它之所以能識(shí)別參數(shù)的個(gè)數(shù),是因?yàn)樗牡谝粋€(gè)參數(shù)中必須要描述后面參數(shù)的格式字符串,這正是一開(kāi)始所提到的第一個(gè)問(wèn)題中說(shuō)到的要注意的問(wèn)題。這也是它被很多人所詬病的原因,但是,本人認(rèn)為這種方式是很好的,后面會(huì)與 java 和 .net 的實(shí)現(xiàn)方式進(jìn)行比較。

三、java 和 .net 實(shí)現(xiàn)可變參數(shù)的方式。

java 從1.5以后,開(kāi)始支持可變參數(shù),其定義語(yǔ)法為:

復(fù)制代碼 代碼如下:

void testMethod(String ... args)

對(duì)于這個(gè)方法,可以這樣調(diào)用:testMethod("gly", "zxy", "ChenFei");

.net 也支持可變參數(shù),其定義語(yǔ)法為:

復(fù)制代碼 代碼如下:

void TestMethod(params string[] args)

對(duì)于這個(gè)方法,可以這樣調(diào)用:TestMethod("gly", "zxy", "ChenFei");

在 java 和 .net 中,對(duì)于可變參數(shù)的實(shí)現(xiàn)基本是一樣的:編譯器在編譯時(shí),將方法簽名中的可變參數(shù)視為相應(yīng)類(lèi)型的數(shù)組,編譯相應(yīng)的調(diào)用時(shí),根據(jù)實(shí)參生成一個(gè)數(shù)組,將參數(shù)裝入到數(shù)組中進(jìn)行傳遞,而在可變參數(shù)方法的方法體中,按使用數(shù)組的方式使用可變參數(shù)。

四、兩種實(shí)現(xiàn)方式的比較

C 語(yǔ)言的實(shí)現(xiàn)方式與 java .net 的實(shí)現(xiàn)方式相比,C 語(yǔ)言需要程序員做更多的工作,而且,確實(shí)增加了出錯(cuò)的機(jī)會(huì),java .net 的實(shí)現(xiàn)方式可以很容易的確定參數(shù)的類(lèi)型和個(gè)數(shù),這些 C 的實(shí)現(xiàn)中是沒(méi)有的,但是 java .net 的實(shí)現(xiàn)方式會(huì)生成臨時(shí)數(shù)組,當(dāng)然 java .net 有垃圾回收機(jī)制,但是,垃圾什么時(shí)候被回收是不確定的,而且是代價(jià)很大的,垃圾回收是個(gè)好東西,但我不喜歡,我認(rèn)為不需要的東西應(yīng)該立即釋放,這是完美的一個(gè)方面的體現(xiàn)。C 中沒(méi)有這個(gè)問(wèn)題,參數(shù)的個(gè)數(shù)和類(lèi)型問(wèn)題可以靠約定或指定來(lái)解決,而這兩個(gè)問(wèn)題在 java 和 .net 中,參數(shù)個(gè)數(shù)其實(shí)是間接傳遞過(guò)去了(數(shù)組的長(zhǎng)度),參數(shù)類(lèi)型則是在方法簽名中約定了。當(dāng)然,java .net 的設(shè)計(jì)目標(biāo)和 C 語(yǔ)言不同,這里說(shuō)多了。

 

相關(guān)文章

  • Qt串口通信開(kāi)發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法

    Qt串口通信開(kāi)發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法

    這篇文章主要介紹了Qt串口通信開(kāi)發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法,需要的朋友可以參考下
    2020-03-03
  • C++11中的default函數(shù)使用

    C++11中的default函數(shù)使用

    這篇文章主要介紹了C++11中的default函數(shù)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • C++實(shí)現(xiàn)STL迭代器萃取的示例代碼

    C++實(shí)現(xiàn)STL迭代器萃取的示例代碼

    迭代器是一種抽象的設(shè)計(jì)概念,它提供了一種方法,使之能夠依序巡訪某個(gè)聚合物(容器)所含的各個(gè)元素,而又無(wú)需暴露該聚合物的內(nèi)部表述方式。本文主要介了STL迭代器萃取的方法,需要的可以參考一下
    2022-11-11
  • Qt5+QMediaPlayer實(shí)現(xiàn)音樂(lè)播放器的示例代碼

    Qt5+QMediaPlayer實(shí)現(xiàn)音樂(lè)播放器的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用Qt5和QMediaPlayer實(shí)現(xiàn)簡(jiǎn)易的音樂(lè)播放器,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2022-12-12
  • Cocos2d-x學(xué)習(xí)筆記之Hello World源碼分析

    Cocos2d-x學(xué)習(xí)筆記之Hello World源碼分析

    這篇文章主要介紹了Cocos2d-x學(xué)習(xí)筆記之Hello World源碼分析,接上一篇內(nèi)容,本文著重分析源碼文件,需要的朋友可以參考下
    2014-09-09
  • ubuntu 下編譯C++代碼出現(xiàn)的問(wèn)題解決

    ubuntu 下編譯C++代碼出現(xiàn)的問(wèn)題解決

    這篇文章主要介紹了ubuntu 下編譯C++代碼出現(xiàn)的問(wèn)題解決的相關(guān)資料,需要的朋友可以參考下
    2015-03-03
  • string,CString,char*之間的轉(zhuǎn)化

    string,CString,char*之間的轉(zhuǎn)化

    下面是MFC/C++/C中字符類(lèi)型CString, int, string, char*之間的轉(zhuǎn)換的說(shuō)明與舉例,經(jīng)常用的東西,相信對(duì)于用C/C++的朋友,還是比較有用的
    2013-03-03
  • php調(diào)用c++的方法

    php調(diào)用c++的方法

    這篇文章主要介紹了php調(diào)用c++的方法,需要的朋友可以參考下
    2014-01-01
  • C++實(shí)現(xiàn)貪心算法的示例詳解

    C++實(shí)現(xiàn)貪心算法的示例詳解

    這篇文章主要通過(guò)幾個(gè)試題為大家詳細(xì)介紹了C++中貪心算法的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)貪心算法有一定的幫助,需要的可以參考一下
    2022-07-07
  • C語(yǔ)言庫(kù)函數(shù)qsort的使用詳解

    C語(yǔ)言庫(kù)函數(shù)qsort的使用詳解

    C語(yǔ)言庫(kù)函數(shù)中的qsort的是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù),這篇文章主要介紹了C語(yǔ)言庫(kù)函數(shù)qsort的使用,需要的朋友可以參考下
    2022-06-06

最新評(píng)論