Quake源碼的一個參數引用技巧

类别:编程语言 点击:0 评论:0 推荐:

       曾經在Quake源碼里看到下面一段代碼: 
int VM_Call( vm_t *vm, int instruction, ... ) {
 va_list argptr;
 int  arg0;
 int  arg1;
 int  arg2;
 int  arg3;
 int  arg4;
 int  arg5;
 int  arg6;

 if( !vm ) {
  Com_Error( ERR_FATAL, "VM_Call with NULL vm" );
 }

 //Com_Printf( "VM_Call( %i )\n", instruction );

 va_start( argptr, instruction );
 arg0 = va_arg( argptr, int );
 arg1 = va_arg( argptr, int );
 arg2 = va_arg( argptr, int );
 arg3 = va_arg( argptr, int );
 arg4 = va_arg( argptr, int );
 arg5 = va_arg( argptr, int );
 arg6 = va_arg( argptr, int );
 va_end( argptr );

 return vm->vmMain( instruction, arg0, arg1, arg2, arg3, arg4, arg5, arg6 );
}
     引起我興趣的是VM_Call( vm_t *vm, int instruction, ... ) 中參數聲明的部分."..."這個關鍵字我是從來沒有看見過,覺得很是新奇.它的作用感覺是要騙過編譯器,使它不對vm_t *vm, int instruction以後所傳入的參數類型和個數進行檢查.換句話說就是不論函數調用者有多少參數,都可以一次性地壓入堆棧中.那麼函數本身如何去利用堆棧中的參數呢?我們還是先去看看下面系統定義的宏:
typedef char *  va_list;
#ifdef  _M_IX86


#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap)      ( ap = (va_list)0 )

#elif   defined(_M_MRX000)


/* Use these types and definitions if generating code for MIPS */

#define va_start(ap,v) ap  = (va_list)&v + sizeof(v)
#define va_end(list)
#define va_arg(list, mode) ((mode *)(list =\
 (char *) ((((int)list + (__builtin_alignof(mode)<=4?3:7)) &\
 (__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]

/*  +++++++++++++++++++++++++++++++++++++++++++
    Because of parameter passing conventions in C:
    use mode=int for char, and short types
    use mode=double for float types
    use a pointer for array types
    +++++++++++++++++++++++++++++++++++++++++++ */


#elif   defined(_M_ALPHA)


        我們知道函數內部使用的變量都是在堆棧中開闢空間,而且堆棧的生長方向是從高地址向低地址生長.如果跳過函數定義的第一個變量,我們就來到了存放參數的棧頂,也就是#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
這個宏的涵義.#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )則是把參數逐個的從堆棧中取出,但並不是出棧,這裡要注意.
        可是為甚麼要採用這種看起來比較彆扭的方式來傳遞參數呢?主要原因是讓代碼能夠在多個編譯器上編譯,並且保證執行效果一樣.每種編譯器所定義的原始數據類型的字节長度也許是不同的.

本文地址:http://com.8s8s.com/it/it27037.htm