这是第2章中的一个例子
码比较好理解...
/*-----------------------------------------------------
   SCRNSIZE.C -- Displays screen size in a message box
                 (c) Charles Petzold, 1998
  -----------------------------------------------------
*/


#include 
<windows.h>
#include 
<tchar.h>     
#include 
<stdio.h>     

int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, )
{
     TCHAR   szBuffer [
1024] ;
     va_list pArgList ;

          
// The va_start macro (defined in STDARG.H) is usually equivalent to:
          
// pArgList = (char *) &szFormat + sizeof (szFormat) ;

     va_start (pArgList, szFormat) ;

          
// The last argument to wvsprintf points to the arguments

     _vsntprintf (szBuffer, 
sizeof (szBuffer) / sizeof (TCHAR), 
                  szFormat, pArgList) ;

          
// The va_end macro just zeroes out pArgList for no good reason

     va_end (pArgList) ;

     
return MessageBox (NULL, szBuffer, szCaption, 0) ;
}


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, 
int iCmdShow) 
{
     
int cxScreen, cyScreen ;
     
//GetSystemMetrics Api function --(SM_CXSCREEN),(SM_CYSCREEN) return weight and high,in pixles,of the screen
     cxScreen = GetSystemMetrics (SM_CXSCREEN) ;
     cyScreen 
= GetSystemMetrics (SM_CYSCREEN) ;

     MessageBoxPrintf (TEXT (
"ScrnSize"), 
                       TEXT (
"The screen is %i pixels wide by %i pixels high."),
                       cxScreen, cyScreen) ;
     
return 0 ;
}


 

第一次看见va_ 不是很理解,variable-argument(可变参数) 搜了一下

----------------------------------------------------

va_list arg_ptr:定义一个指向个数可变的参数列表指针;

  

  va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一个固定参数;…之前的一个参数),函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。如果有一va函数的声明是void va_test(char a, char b, char c, ),则它的固定参数依次是a,b,c,最后一个固定参数argNc,因此就是va_start(arg_ptr, c)

  

  va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中下一个参数。

  

  va_copy(dest, src)destsrc的类型都是va_listva_copy()用于复制参数列表指针,将dest初始化为src

  

  va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_ptr被置无效后,可以通过调用va_start()va_copy()恢复arg_ptr。每次调用va_start() / va_copy()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() va_end()之内。

  

  

  

  ◎用法:

  func( Type para1, Type para2, Type para3, ... )

  {

   /****** Step 1 ******/

   va_list ap;

   va_start( ap, para3 ); //一定要“...”之前的那个参数

  

   /****** Step 2 ******/

   //此时ap指向第一个可变参数

   //调用va_arg取得里面的值

  

   Type xx = va_arg( ap, Type );

  

   //Type一定要相同,如:

   //char *p = va_arg( ap, char *);

   //int i = va_arg( ap, int );

  

   //如果有多个参数继续调用va_arg

  

   /****** Step 3 ******/

   va_end(ap); //For robust!

  }

  

  ◎研究:

  typedef char * va_list;

  

  #define va_start _crt_va_start

  #define va_arg _crt_va_arg

  #define va_end _crt_va_end

  

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

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

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

  va_list argptr;

  C语言的函数是从右向左压入堆栈的,调用va_start后,

  按定义的宏运算,_ADDRESSOF得到v所在的地址,然后这个

  地址加上v的大小,则使ap指向第一个可变参数如图:

  

   栈底 高地址

   | .......

   | 函数返回地址

   | .......

   | 函数最后一个参数

   | ....

   | 函数第一个可变参数 <--va_startap指向

   | 函数最后一个固定参数

   | 函数第一个固定参数

   栈顶 低地址

  

  

  然后,用va_arg()取得类型t的可变参数值, 先是让ap指向下一个参数:

  ap += _INTSIZEOF(t),然后在减去_INTSIZEOF(t),使得表达式结果为

  ap之前的值,即当前需要得到的参数的地址,强制转换成指向此参数的

  类型的指针,然后用*取值

  

  最后,用va_end(ap),给ap初始化,保持健壮性。

 

  一般的用法是这样(个人理解)

  va_list args; //声明变量

  va_start(args, fmt); //开始解析。args指向fmt后面的参数

  TYPE var = va_arg(args, TYPE); //取下一个参数并返回。args指向下一个参数

va_end(args); //结束解析

//自己写了l个Demo
  #include   <stdio.h>
  #include   
<stdarg.h>

  
#define   SAMPLEFOOPARAM2DEFAULT   255
  
#define   EOL   -1

  
int   samplefoo(int   a,   )
  
{
      
int   param1,   param2,param3;
      va_list   num_ptr;

      param1   
=   a;
      va_start(num_ptr,   a);
      
/*   假定这里的第二个参数是int型,如果没有就用default值   */
      param2   
=   va_arg(num_ptr,   int);
      
if   (   param2     ==   EOL   )
          param2   
=   SAMPLEFOOPARAM2DEFAULT;
      param3
=va_arg(num_ptr,int);
      
if(param3==EOL)
          param3
=SAMPLEFOOPARAM2DEFAULT;
      va_end(num_ptr);
      printf(
"The   first   parameter   of   function   is:   %d\n",   param1);
      printf(
"The   first   parameter   of   function   is:   %d\n",   param2);
      printf(
"The   first   parameter   of   function   is:   %d\n",   param3);
      
return   0;
  }


  
int   main()
  
{
      printf(
"Output   of   samplefoo(10,   20,   EOL):\n");
      samplefoo(
10,   20,  30,  EOL);   /*   使用的不是缺省值,注意最后的EOL是一个标记值   */

      printf(
"Output   of   samplefoo(10,   EOL):\n");
      samplefoo(
10,   EOL,EOL);   /*   使用的不是缺省值,注意最后的EOL是一个标记值   */

      
return   0;
  }


附上另一个Demo
#include <stdio.h>

#include 
<stdlib.h>

void myprintf(char* fmt, )        //一个简单的类似于printf的实现,//参数必须都是int 类型



    
char* pArg=NULL;               //等价于原来的va_list 

    
char c;

    

    pArg 
= (char*&fmt;          //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值

    pArg 
+= sizeof(fmt);         //等价于原来的va_start          

 

    
do

    
{

        c 
=*fmt;

        
if (c != '%')

        
{

            putchar(c);            
//照原样输出字符

        }


        
else

{

//按格式字符输出数据

            
switch(*++fmt) 

{

            
case 'd':

                printf(
"%d",*((int*)pArg));           

                
break;

            
case 'x':

                printf(
"%#x",*((int*)pArg));

                
break;

            
default:

                
break;

            }
 

            pArg 
+= sizeof(int);               //等价于原来的va_arg

        }


        
++fmt;

    }
while (*fmt != '\0'); 

    pArg 
= NULL;                               //等价于va_end

    
return

}


int main(int argc, char* argv[])

{

    
int i = 1234;

    
int j = 5678;

    

    myprintf(
"the first test:i=%d\n",i,j); 

    myprintf(
"the secend test:i=%d; %x;j=%d;\n",i,0xabcd,j); 

    system(
"pause");

    
return 0;

}


在intel
+win2k+vc6的机器执行结果如下:

the first test:i
=1234

the secend test:i
=12340xabcd;j=5678;