va_list 原理以及用法

VA_LIST 是在C語言中解決變參問題的一組宏,變參問題是指參數的個數不定,可以是傳入一個參數也可以是多個;可變參數中的每個參數的類型可以不同,也可以相同;可變參數的每個參數並沒有實際的名稱與之相對應,用起來是很靈活。

va_list 用法示例

#include <stdarg.h> 

int AveInt(int,...);

 void main()
{
   printf("%d/t",AveInt(2,2,3));
   printf("%d/t",AveInt(4,2,4,6,8));

   return;
}

int AveInt(int v,...)
{
   int ReturnValue=0;
   int i=v;

   va_list ap ;
   va_start(ap,v);

   while(i>0)
   {
       ReturnValue+=va_arg(ap,int) ;
       i--;
   }
   va_end(ap); 
   return ReturnValue/=v;
}

VA_LIST的用法:
(1)首先在函數裏定義一具VA_LIST型的變量,這個變量是指向參數的指針;
(2)然後用VA_START宏初始化變量剛定義的VA_LIST變量;
(3)然後用VA_ARG返回可變的參數,VA_ARG的第二個參數是你要返回的參數的類型(如果函數有多個可變參數的,依次調用VA_ARG獲取各個參數);
(4)最後用VA_END宏結束可變參數的獲取。

上面是va_list的具體用法,下面講解一下va_list各個語句含義(如上示例黑體部分)和va_list的實現。

     可變參數是由宏實現的,但是由於硬件平臺的不同,編譯器的不同,宏的定義也不相同,下面是VC6.0中x86平臺的定義 :

      typedef char * va_list;     // TC中定義爲void*
      #define _INTSIZEOF(n)    ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //爲了滿足需要內存對齊的系統
      #define va_start(ap,v)    ( ap = (va_list)&v + _INTSIZEOF(v) )     //ap指向第一個變參的位置,即將第一個變參的地址賦予ap
      #define va_arg(ap,t)       ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   /*獲取變參的具體內容,t爲變參的類型,如有多個參數,則通過移動ap的指針來獲得變參的地址,從而獲得內容*/
      #define va_end(ap) ( ap = (va_list)0 )   //清空va_list,即結束變參的獲取

va_list ap ; 定義一個va_list變量ap
va_start(ap,v) ;執行ap = (va_list)&v + _INTSIZEOF(v),ap指向參數v之後的那個參數的地址,即 ap指向第一個可變參數在堆棧的地址。
va_arg(ap,t) , ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出當前ap指針所指的值,並使ap指向下一個參數。 ap+= sizeof(t類型),讓ap指向下一個參數的地址。然後返回ap-sizeof(t類型)的t類型指針,這正是第一個可變參數在堆棧裏的地址。然後 用取得這個地址的內容。
va_end(ap) ; 清空va_list ap。

使用VA_LIST應該注意的問題:
(1)因爲va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變參數的類型和個數完全在該函數中由程序代碼控制,它並不能智能地識別不同參數的個數和類型. 也就是說,你想實現智能識別可變參數的話是要通過在自己的程序裏作判斷來實現的.
(2)另外有一個問題,因爲編譯器對可變參數的函數的原型檢查不夠嚴格,對編程查錯不利.不利於我們寫出高質量的代碼。
(3)由於參數的地址用於VA_START宏,所以參數不能聲明爲寄存器變量,或作爲函數或數組類型。

參考文章:
http://blog.csdn.net/aihao1984/article/details/5953668

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章