變參函數的實現

來源:互聯網
上載者:User

相對於固定參數的函數,變參函數的可用性無疑是更好的。我們最常用的變參函數包括scanf和printf。剛剛接觸到變參函數的時候,我覺得這太神奇了,它並不知道我要輸入什麼類型的資料,要輸入多少個資料,卻能完美地處理。其實,可變參數機制實現起來是相當容易的(在stdarg.h的基礎上),而且,它的作用並沒有想象中的那麼神奇。

         可變參數機制並不能擷取某次輸入的所有參數的個數,也不能自己確定每一個輸入參數的類型。嗯,沒錯,看上去printf和scanf就能知道每次輸入的參數個數和每個參數的類型。其實,仔細想一想就會發現printf和scanf沒這個本事,輸入的參數個數和每個參數的類型是使用者在format內容中,通過%模式等告訴編譯器的。光是這麼說可能不夠取信於人,以簡化的printf為例,讓我們看一段K&R中的常式吧:

#include
<stdarg.h>

/* minprintf: minimal printf with variable argument  list */

void
minprintf(char *fmt,  ...)

{

    va_list ap;
/* points to each unnamed arg in turn */

    char  *p,  *sval;

    int  
ival;

    double  
dval;

    va_start(ap, fmt);
/* make ap point to 1st unnamed arg */

    for  (p =
fmt;  *p;
p++)  {

       if  (*p !=
'%')  {

           putchar(*p);

           continue;

       }

       switch  (*++p) {

           case  
'd':

              ival = va_arg(ap, int);

              printf("%d",  
ival);

              break;

           case  
'f':

              dval =
va_arg(ap, double);

              printf("%f",  
dval);

              break;

           case  
's':

              for (sval =
va_arg(ap,
char *); *sval;
sval++)

                  putchar(*sval);

              break;

           default:

              putchar(*p);

              break;

       }

    }

    va_end(ap);
/* clean up when done */

}

可以看出,printf處理可變參數的關鍵就在於它的參數char *fmt。注意程式中的for迴圈,指標p正是一次次地根據fmt中的提示(在這個簡化的例子中是%)經由switch分支來確定下一個參數的類型和有效參數的個數。

         所以說,可變參數不是萬能的,它只是一種很normal的機制,不過正是先驅們那化腐朽為神奇的想象力,藉由這種normal的機制實現了神奇而又令人詬病的scanf和printf函數。一般而言,除了像printf一樣,在函數中實現一個特殊的format之外,一些可變參數中的參數類型都是一致的(比如說只可能都是int)函數,則會在之前的固定參數中用一個參數指出可變參數的個數或是類型。下面是一個C Primer Plus中的常式:

#include
<stdarg.h>

double
sum(int lim,...)

{

    va_list ap;                  
//  declare object to hold arguments

    double  
tot = 0;

    int  
i;

    va_start(ap, lim);           
//  initialize ap to argument list

    for  (i = 0;
i  <  lim;
i++)

       tot  += va_arg(ap,  double);
// access each item in argument  list

    va_end(ap);                  
//  clean up

    return  
tot;

}

可以看出,函數sum中的固定參數lim指出了可變參數的個數。

         以上介紹了可變參數機制實現的兩種過程:自訂format和前置指示標誌。接下來就會詳細解釋在C語言中如何具體實現可變參數機制。從兩個常式可以看出,有四個宏是至關重要的:

va_list

va_start(va_list, lastpar)

va_arg(va_list, Type)

va_end(va_list)

va_list:一個char鏈表(實際上應該是一個連續的記憶體塊,像數組一樣),在使用時表現為一個指向char類型的指標;

va_start:初始化va_list。通過最後的固定參數實現對可變參數初始位置的定位,並為va_list分配記憶體,將可變參數複製該記憶體塊中,使va_list指向該記憶體塊的初始位置;

va_arg:通過移動指標va_list擷取由參數Type指定的變數並返回該變數。

va_end:釋放va_list擁有的記憶體塊所佔據的記憶體空間。

看,一切不就一清二楚了嗎?不過,還有如下幾個問題還需要特別注意一下:

1>     C標準規定實現可變參數機制的函數至少要有一個固定參數。從上面的討論可以看出,這無論是從文法上還是實現上都是必須的。

2>     隱式類型轉換不可用。比如說,va_arg指定了類型是double,若傳入的是int的變數10就會出錯。

3>     C語言的整型提升原則。也就是說,在va_arg中擷取float和double使用的Type都是double,而擷取char、short和int使用的Type都是int。

函數指標不可用,除非用typedef定義過。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.