深入剖析變長參數函數的實現

來源:互聯網
上載者:User

什麼是變長參數?

所謂含有變長參數的函數是指該函數可以接受可變數目的形參。例如我們都非常熟悉的

printf,scanf等等。

2:變長參數如何??

首先來看下面這樣一個例子:

#include<stdio.h>
#include<stdarg.h>
#include<string.h>

void demo(char *msg,...)
{
 va_list argp;
 int arg_number=0;
 char *para = msg;
 va_start(argp,msg);
 while(1)
 {
  if ( strcmp( para, "/0") != 0 )
  {
   arg_number++;   
   printf("parameter %d is: %s/n",arg_number,para); 
   
  }
  else
   break; 
  para = va_arg(argp,char *);
 }
 va_end(argp); 
}
int main()
{
 demo("Hello","World","/0");
 system("pause");
 return 0;
}

實現這樣一個函數要在內部使用va_list,va_start,va_arg,va_end,這些都是定義在

stdarg.h中的宏。

va_list是定義了一個儲存函數參數的資料結構。

va_start(argp,msg)是將argp指向第一個可變參數,而msg是最後一個確定的參數。

最後一個確定的參數的含義是指它以後的參數都是可變參數,如果有下面的函式宣告

void demo(char *msg1,char *msg2,...)

那麼這裡的最後一個確定參數就是msg2。

va_arg(argp,char *)返回當前參數的值,類型為char *,然後將argp指向下一個變長參

數。從這一步可以看出來我們可以通過va_start和va_arg遍曆所有的變長參數。

va_end 將argp的值置為0。

下面我們看看上述幾個宏在visual c++.net 2003 中的實現方法。首先是va_list的實現

#ifdef  _M_ALPHA
typedef struct {
        char *a0;       /* pointer to first homed integer argument */
        int offset;     /* byte offset of next parameter */
} va_list;
#else
typedef char *  va_list;
#endif

可以看到va_list實際上是一個機器類型相關的宏,除了alpha機器以外,其他機器類

型都被定義為一個char類型的指標變數,之所以定義為char *是因為可以用該變數逐

地址也就是逐位元組對參數進行遍曆。

從上面可以看到,這些宏的實現都是和機器相關的,下面是大家常用的IX86機器下宏的

相關定義。

#elif   defined(_M_IX86)

#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 )

#ifdef  __cplusplus
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v)   ( &(v) )
#endif

首先看_INTSIZEOF(n)

我們知道對於IX86,sizeof(int)一定是4的整數倍,所以~(sizeof(int) - 1) )的值一定是

右面[sizeof(n)-1]/2位為0,整個這個宏也就是保證了右面[sizeof(n)-1]/2位為0,其餘位置

為1,所以_INTSIZEOF(n)的值只有可能是2,4,8,16,......等等,實際上是實現了位元組對齊。

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

所以va_start(ap,v)的作用就很明了了,_ADDRESSOF(v)定義了v的起始地址,_INTSIZEOF(v)定義了v所

佔用的記憶體,所以ap 就指向v後面的參數的起始地址。

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

ap += _INTSIZEOF(t) 使ap指向了後面一個參數的地址

而( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )相當於返回了目前t類型的參數的值。

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

將變數ap 的值置為0。

通過上述分析,再次印證了我麼前面對可變參數實現的解釋。

因此我們可以總結出變長參數函數的一般實現方法:

1:聲明原型,形如void demo(char *msg,...),注意變長參數的原型聲明中至少要含有

一個確定參數。

2:用va_list定義儲存函數參數的資料結構,可以理解為一個指標變數(稍後會解釋)。

3:用va_start將上一步定義的變數指向第一個可變參數。

4:用va_arg遍曆所有的可變參數。

5:用va_end將指標變數持有的地址值置為0。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.