这是第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;