小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

如何寫(xiě)參數(shù)個(gè)數(shù)不定的函數(shù)(即類似printf的函數(shù))

 看風(fēng)景D人 2014-09-11
#include <stdio.h>
#include <stdarg.h>        //要包含這個(gè)頭文件
void variable(int i, ...)
{
    int j = 0
;
      va_list arg_ptr;    //第1步,定義這個(gè)指向參數(shù)列表的變量

      va_start(arg_ptr, i);//第2步,把上面這個(gè)變量初始化.即讓它指向參數(shù)列表

    while( j != -1 )
      {
        //第3步,獲取arg_ptr指向的當(dāng)前參數(shù).這個(gè)參數(shù)的類型由va_arg的第2個(gè)參數(shù)指定

          j = va_arg(arg_ptr, int);
          printf("%d "
,j );
      }
      va_end(arg_ptr);    //第4步,做一些清理工作

}
main()
{
      variable(3, 3, 4, 5, 6, -1
);
}
//////////////////////////
//////////////////////////
//////////////////////////
///////作為va_list,va_start的練習(xí),可以學(xué)習(xí)一下使用_vsnprintf函數(shù)
#include <stdio.h>
#include <stdarg.h>

 

 

void formatoutput(char* format, ...)
{
    char s[10
];
      va_list arg_ptr;
      va_start(arg_ptr, format);
      _vsnprintf(s, sizeof(s)-1
, format, arg_ptr);
      printf("%s "
, s);

 

}

 

void main()
{
      formatoutput("%s%s", "d", "g"
);
}
 
////////////////////
/////////////////////
參數(shù)個(gè)數(shù)不定的函數(shù),最頻繁使用的就是printf()與scanf()。其實(shí),我們也可以自己實(shí)現(xiàn)這樣的功能,首先看
一個(gè)例子:
#include <stdio.h>
#include <stdarg.h>
int Sum(int first, int second, ...)
{
       int sum = 0, t = first;
       va_list vl;
       va_start(vl, first);
       while (t != -1)
       {
         sum += t;
         t = va_arg(vl, int);//將當(dāng)前參數(shù)轉(zhuǎn)換為int類型
      }
      va_end(vl);
      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;
}
在上面的例子中,實(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)思想。
        明白了以上原理之后,就可以完全不使用宏,自己實(shí)現(xiàn)這樣的一個(gè)功能了,下面的程序是我按照上面的
思想改寫(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é)束符
{
        sum += *(int *)vl;//類型轉(zhuǎn)換
        vl += sizeof(int);//移動(dòng)指針,使指針指向下一個(gè)參數(shù)
}
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, ...)
{
    va_list vl;
    va_start(vl, frm);
    vprintf(frm, vl);
    va_end(vl);
}
 
//////////////////////
////////////////////////
書(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)意義?
還有就是不明白
int printf(const char*...);
printf("hello,&s\n",userName);

這個(gè)c的輸出函數(shù)是怎么用(...)實(shí)現(xiàn)的.

先謝了:)

Re:

首先函數(shù)體中聲明一個(gè)va_list,然后用va_start函數(shù)來(lái)獲取參數(shù)列表中的參數(shù),使用完畢后調(diào)用va_end()結(jié)束。像這段代碼:
void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
{
va_list args;
va_start(args, pszFormat);
_vsnprintf(pszDest, DestLen, pszFormat, args);
va_end(args);
}

///////////////////////////

一個(gè)簡(jiǎn)單的可變參數(shù)的C函數(shù)

      先看例子程序。該函數(shù)至少有一個(gè)整數(shù)參數(shù),其后占位符…,表示后面參數(shù)的個(gè)數(shù)不定。在這個(gè)例子里,所有的輸入?yún)?shù)必須都是整數(shù),函數(shù)的功能只是打印所有參數(shù)的值。函數(shù)代碼如下:

//示例代碼1:可變參數(shù)函數(shù)的使用
#include "stdio.h"
#include "stdarg.h"
void simple_va_fun(int start, ...)
{
     va_list arg_ptr;
     int nArgValue =start;
     int nArgCout=0;   //可變參數(shù)的數(shù)目
     va_start(arg_ptr,start);   //以固定參數(shù)的地址為起點(diǎn)確定變參的內(nèi)存起始地址。
     do
     {
         ++nArgCout;
         printf("the %d th arg: %d",nArgCout,nArgValue); //輸出各參數(shù)的值
         nArgValue = va_arg(arg_ptr,int);   //得到下一個(gè)可變參數(shù)的值
     } while(nArgValue != -1);                
     return;
}
int main(int argc, char* argv[])
{
     simple_va_fun(100,-1);
     simple_va_fun(100,200,-1);
     return 0;
}

下面解釋一下這些代碼。從這個(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ù)的值。


⑸設(shè)定結(jié)束條件,這里的條件就是判斷參數(shù)值是否為-1。注意被調(diào)的函數(shù)在調(diào)用時(shí)是不知道可變參數(shù)的正確數(shù)目的,程序員必須自己在代碼中指明結(jié)束條件。至于為什么它不會(huì)知道參數(shù)的數(shù)目,在看完這幾個(gè)宏的內(nèi)部實(shí)現(xiàn)機(jī)制后,自然就會(huì)明白。

 

1)可變參數(shù)的存儲(chǔ)形式.
大家都知道,一般函數(shù)的形參屬于局部變量.而局部變量就是存儲(chǔ)在內(nèi)存的棧區(qū)(所謂的棧區(qū):由編譯器自動(dòng)分配釋放,
存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。).可變參數(shù)也是存儲(chǔ)在內(nèi)存棧區(qū).
在對(duì)函數(shù)的形參存儲(chǔ)的時(shí)侯,編譯器是從函數(shù)的形參的右邊到左邊逐一地壓棧,
這樣保證了棧頂是函數(shù)的形參的第一個(gè)參數(shù)(從左到右數(shù)).而80x86平臺(tái)下的內(nèi)存分配順序是從高地址內(nèi)存到低地址內(nèi)存.
因此,函數(shù)的形參在內(nèi)存的存儲(chǔ)形式如下圖(以fun(int var1,int var2,...,int var3,int var4)為例):
棧區(qū):
|棧頂             低地址
|第一個(gè)固定參數(shù)var1
|可變參數(shù)前的第一個(gè)固定參數(shù)var2
|可變參數(shù)的第一個(gè)參數(shù)
|...
|可變參數(shù)的最后一個(gè)參數(shù)
|函數(shù)的倒數(shù)第二個(gè)固定參數(shù)var3
|函數(shù)的最后一個(gè)固定參數(shù)var4
|...
|函數(shù)的返回地址
|...
|棧底    高地址

 

原文地址:http://archive.cnblogs.com/a/1732006/

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多