|
#include <stdio.h>
#include <stdarg.h> void variable(int i, ...) { } main() { } //////////////////////////
//////////////////////////
//////////////////////////
///////作為va_list,va_start的練習(xí),可以學(xué)習(xí)一下使用_vsnprintf函數(shù)
#include
<stdio.h>
#include <stdarg.h> { } { } ////////////////////
/////////////////////
參數(shù)個(gè)數(shù)不定的函數(shù),最頻繁使用的就是printf()與scanf()。其實(shí),我們也可以自己實(shí)現(xiàn)這樣的功能,首先看
一個(gè)例子:
#include
<stdio.h>
#include
<stdarg.h>
int Sum(int first,
int second, ...)
{
} int main(int argc,
char* argv[])
{ } 在上面的例子中,實(shí)現(xiàn)了一個(gè)參數(shù)個(gè)數(shù)不定的求int型和的函數(shù)Sum()。函數(shù)中一開(kāi)始定義了一個(gè)va_list型變量
vl,該變量用來(lái)訪問(wèn)可變參數(shù),實(shí)際上就是指針,接著使用va_start使vl指向第一個(gè)參數(shù),然后再使用va_arg
來(lái)遍歷每一個(gè)參數(shù),va_arg返回參數(shù)列表中的當(dāng)前參數(shù)并使vl指向參數(shù)列表中的下一個(gè)參數(shù)。最后通過(guò)va_end
把vl指針清為NULL。在這里,va_start,va_arg,va_end其實(shí)都是宏,那么這些宏又是如何實(shí)現(xiàn)他們的功能的
呢?
一個(gè)很顯然的問(wèn)題就是既然參數(shù)個(gè)數(shù)在函數(shù)調(diào)用之前沒(méi)有確定,所以,在函數(shù)定義的時(shí)候沒(méi)有辦法像普通函數(shù)
那樣使用確定的變量來(lái)接受傳進(jìn)來(lái)的參數(shù),于是,問(wèn)題的關(guān)鍵就是如何接收到這些不確定的參數(shù)了?首先,看
看函數(shù)調(diào)用時(shí)實(shí)際發(fā)生的情況,在函數(shù)調(diào)用的時(shí)候,使用棧傳遞參數(shù),拿上例來(lái)說(shuō),如果調(diào)用Sum(30,
20, 10,
-1),那么參數(shù)入棧后的情形如下圖所示(假定按照自右至左的順序入棧):
既然參數(shù)在棧中的情形已經(jīng)知道了,那么,如果使用指針(程序中的vl)指向第一個(gè)參數(shù)(va_start(vl,
first)),因?yàn)樗袇?shù)都是連續(xù)存放的,通過(guò)移動(dòng)指針就可以訪問(wèn)到每一個(gè)參數(shù)了(va_arg(vl,
int)),這
就是我在程序中使用到那幾個(gè)宏的實(shí)現(xiàn)思想。
思想改寫(xiě)的:
#include
<stdio.h>
int Sum(int first,
int second, ...)
{ int sum = 0, t = first; char * vl;//定義一個(gè)指針 vl = (char *)&first;//使指針指向第一個(gè)參數(shù) while (*vl != -1)//-1是預(yù)先給定的結(jié)束符 { } return sum; } int main(int argc,
char* argv[])
{ printf("The sum is %d\n", Sum(30, 20, 10, -1));//-1是參數(shù)結(jié)束標(biāo)志 return 0; } 可以看出,通過(guò)使用指針的確實(shí)現(xiàn)了參數(shù)個(gè)數(shù)不定的函數(shù)了,但是程序中還有一個(gè)問(wèn)題,那就是移動(dòng)指針,在 程序中因?yàn)槲沂褂玫膮?shù)都是相同的int類型,所以可以事先知道訪問(wèn)下一個(gè)參數(shù)的時(shí)候應(yīng)該移動(dòng)sizeof(int)
個(gè)字節(jié),但是,如果參數(shù)的類型不同呢?這的確是個(gè)比較麻煩的問(wèn)題,因?yàn)椴煌臄?shù)據(jù)類型占用的字節(jié)數(shù)可能
是不一樣的(如double型為8個(gè)字符,short
int型為2個(gè)),所以很難事先確定應(yīng)該移動(dòng)多少個(gè)字節(jié)!但是辦法
還是有的,這就是使用指針了,無(wú)論什么類型的指針,都是占用4個(gè)字節(jié),所以,可以把所有的傳人參數(shù)都設(shè)置
為指針,這樣一來(lái),就可以通過(guò)移動(dòng)固定的4個(gè)字節(jié)來(lái)實(shí)現(xiàn)遍歷可變參數(shù)的目的了,至于如果取得指針中的內(nèi)容
并使用他們,當(dāng)然也是無(wú)法預(yù)先得知的了,所以這大概也就是像printf(),scanf()之類的函數(shù)還需要一個(gè)格式
控制符的原因吧^_^!不過(guò)實(shí)現(xiàn)起來(lái)還是有不少麻煩,暫且盜用vprintf()來(lái)實(shí)現(xiàn)一個(gè)與printf()函數(shù)一樣功能的
函數(shù)了,代碼如下:
void myPrint(const
char *frm, ...)
{ } //////////////////////
////////////////////////
書(shū)上說(shuō),當(dāng)無(wú)法列出傳遞函數(shù)的所有實(shí)參的類型和數(shù)目時(shí),可用省略號(hào)指定參數(shù)表(...)
如:void foo(...); void foo(parm_list,...); void foo(...) { } 調(diào)用:foo(a,b,c); 就是不懂,把a(bǔ),b,c的值傳進(jìn)函數(shù)里面后,用什么變量來(lái)接收???如果不能接收,(...)豈不是沒(méi)意義? 這個(gè)c的輸出函數(shù)是怎么用(...)實(shí)現(xiàn)的. 先謝了:) Re: 首先函數(shù)體中聲明一個(gè)va_list,然后用va_start函數(shù)來(lái)獲取參數(shù)列表中的參數(shù),使用完畢后調(diào)用va_end()結(jié)束。像這段代碼: ///////////////////////////
一個(gè)簡(jiǎn)單的可變參數(shù)的C函數(shù) //示例代碼1:可變參數(shù)函數(shù)的使用 下面解釋一下這些代碼。從這個(gè)函數(shù)的實(shí)現(xiàn)可以看到,我們使用可變參數(shù)應(yīng)該有以下步驟:
⑴由于在程序中將用到以下這些宏: void va_start( va_list arg_ptr, prev_param ); type va_arg( va_list arg_ptr, type ); void va_end( va_list arg_ptr ); va在這里是variable-argument(可變參數(shù))的意思。 這些宏定義在stdarg.h中,所以用到可變參數(shù)的程序應(yīng)該包含這個(gè)頭文件。
⑵函數(shù)里首先定義一個(gè)va_list型的變量,這里是arg_ptr,這個(gè)變量是存儲(chǔ)參數(shù)地址的指針.因?yàn)榈玫絽?shù)的地址之后,再結(jié)合參數(shù)的類型,才能得到參數(shù)的值。 ⑶然后用va_start宏初始化⑵中定義的變量arg_ptr,這個(gè)宏的第二個(gè)參數(shù)是可變參數(shù)列表的前一個(gè)參數(shù),即最后一個(gè)固定參數(shù)。 ⑷然后依次用va_arg宏使arg_ptr返回可變參數(shù)的地址,得到這個(gè)地址之后,結(jié)合參數(shù)的類型,就可以得到參數(shù)的值。
1)可變參數(shù)的存儲(chǔ)形式. |
|
|
來(lái)自: 看風(fēng)景D人 > 《面試題2》