這是第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,最后一個固定參數argN為c,因此就是va_start(arg_ptr, c)。
va_arg(arg_ptr, type):返回參數列表中指針arg_ptr所指的參數,返回類型為type,并使指針arg_ptr指向參數列表中下一個參數。
va_copy(dest, src):dest,src的類型都是va_list,va_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_start后ap指向
| 函數最后一個固定參數
| 函數第一個固定參數
棧頂 低地址
然后,用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=1234; 0xabcd;j=5678;