vc++不定参数函数
不定数量参数的实现 - 每天进步一点点... - CSDN博客 每天进步一点点...——C/C++, Linux,
/*-----------------------------------------------------
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 ;
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 ;
}
这应该是以前学C语言时应该掌握的知识,但是亡羊补牢,为时不晚。
例如int printf( const char *format [, argument]... );
int wprintf( const wchar_t *format [, argument]... );
就是这样的函数,在函数的参数列表中,参数的个数是不定的,依据我们的需要而定。我们应该怎样写自己的函数让它随我们的需要而改变参
数的个数,还有我们应该在什么情况下用到这样的函数?
首先,查看msdn,其中有一组宏va_alist专门用作这类函数的实现。下面是msdn的部分文档:
va_arg, va_end, va_start
Access variable-argument lists.
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
void va_start( va_list arg_ptr ); (UNIX version)
void va_start( va_list arg_ptr, prev_param ); (ANSI version)
RoutineRequired HeaderOptional HeadersCompatibility
va_arg<stdio.h> and <stdarg.h><varargs.h>1ANSI, Win 95, Win NT
va_end<stdio.h> and <stdarg.h><varargs.h>1ANSI, Win 95, Win NT
va_start<stdio.h> and <stdarg.h><varargs.h>1ANSI, Win 95, Win NT
1 Required for UNIX V compatibility.
For additional compatibility information, see Compatibility in the Introduction.
Libraries
LIBC.LIBSingle thread static library, retail version
LIBCMT.LIBMultithread static library, retail version
MSVCRT.LIBImport library for MSVCRT.DLL, retail version
Return Value
va_arg returns the current argument; va_start and va_end do not return values.
Parameters
type
Type of argument to be retrieved
arg_ptr
Pointer to list of arguments
prev_param
Parameter preceding first optional argument (ANSI only)
Remarks
The va_arg, va_end, and va_start macros provide a portable way to access the
arguments to a function when the function takes a variable number of arguments.
Two versions of the macros are available: The macros defined in STDARG.H conform
to the ANSI C standard, and the macros defined in VARARGS.H are compatible with
the UNIX System V definition. The macros are:
va_alist
Name of parameter to called function (UNIX version only)
va_arg
Macro to retrieve current argument
va_dcl
Declaration of va_alist (UNIX version only)
va_end
Macro to reset arg_ptr
va_list
typedef for pointer to list of arguments defined in STDIO.H
va_start
Macro to set arg_ptr to beginning of list of optional arguments (UNIX version
only)
Both versions of the macros assume that the function takes a fixed number of
required arguments, followed by a variable number of optional arguments. The
required arguments are declared as ordinary parameters to the function and can
be accessed through the parameter names. The optional arguments are accessed
through the macros in STDARG.H or VARARGS.H, which set a pointer to the first
optional argument in the argument list, retrieve arguments from the list, and
reset the pointer when argument processing is completed.
The ANSI C standard macros, defined in STDARG.H, are used as follows:
All required arguments to the function are declared as parameters in the usual
way. va_dcl is not used with the STDARG.H macros.
va_start sets arg_ptr to the first optional argument in the list of arguments
passed to the function. The argument arg_ptr must have va_list type. The
argument prev_param is the name of the required parameter immediately
preceding the first optional argument in the argument list. If prev_param is
declared with the register storage class, the macro’s behavior is undefined.
va_start must be used before va_arg is used for the first time.
va_arg retrieves a value of type from the location given by arg_ptr and
increments arg_ptr to point to the next argument in the list, using the size
of type to determine where the next argument starts. va_arg can be used any
number of times within the function to retrieve arguments from the list.
After all arguments have been retrieved, va_end resets the pointer to NULL.
The UNIX System V macros, defined in VARARGS.H, operate somewhat differently:
Any required arguments to the function can be declared as parameters in the
usual way.
The last (or only) parameter to the function represents the list of optional
arguments. This parameter must be named va_alist (not to be confused with
va_list, which is defined as the type of va_alist).
va_dcl appears after the function definition and before the opening left brace
of the function. This macro is defined as a complete declaration of the
va_alist parameter, including the terminating semicolon; therefore, no
semicolon should follow va_dcl.
Within the function, va_start sets arg_ptr to the beginning of the list of
optional arguments passed to the function. va_start must be used before va_arg
is used for the first time. The argument arg_ptr must have va_list type.
va_arg retrieves a value of type from the location given by arg_ptr and
increments arg_ptr to point to the next argument in the list, using the size
of type to determine where the next argument starts. va_arg can be used any
number of times within the function to retrieve the arguments from the list.
After all arguments have been retrieved, va_end resets the pointer to NULL.
Example
/* VA.C: The program below illustrates passing a variable
* number of arguments using the following macros:
* va_start va_arg va_end
* va_list va_dcl (UNIX only)
*/
#include <stdio.h>
#define ANSI /* Comment out for UNIX version */
#ifdef ANSI /* ANSI compatible version */
#include <stdarg.h>
int average( int first, ... );
#else /* UNIX compatible version */
#include <varargs.h>
int average( va_list );
#endif
void main( void )
{
/* Call with 3 integers (-1 is used as terminator). */
printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );
/* Call with 4 integers. */
printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );
/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
}
/* Returns the average of a variable list of integers. */
#ifdef ANSI /* ANSI compatible version */
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
#else /* UNIX compatible version must use old-style definition. */
int average( va_alist )
va_dcl
{
int i, count, sum;
va_list marker;
va_start( marker ); /* Initialize variable arguments. */
for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
sum += i;
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
#endif
Output
Average is: 3
Average is: 8
Average is: 0
它用一个求任意个数的平均值为例,讲解了va_alist的用法,阐述了不定个数参数类型函数的实现方法。
这组宏的定义在stdarg.h文件里。
阅读此文件可以了解va_alist这组宏的实现方法,从深层次的理解它的用法,从而做到它为我用,而且不为表面形式所局限。
首先看va_list的定义:
#ifndef _VA_LIST_DEFINED
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else /* _M_ALPHA */
typedef char * va_list;
#endif /* _M_ALPHA */
#define _VA_LIST_DEFINED
#endif /* _VA_LIST_DEFINED */
假如参数只有一个那么va_list就是字符串指针,否则的话它是一个字符串指针和一个偏移的组合结构体。它用来存放这些参数。
再看va_arg、va_start、va_end的定义:
#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)
/* Use these types and definitions if generating code for ALPHA */
/*
* The Alpha compiler supports two builtin functions that are used to
* implement stdarg/varargs. The __builtin_va_start function is used
* by va_start to initialize the data structure that locates the next
* argument. The __builtin_isfloat function is used by va_arg to pick
* which part of the home area a given register argument is stored in.
* The home area is where up to six integer and/or six floating point
* register arguments are stored down (so they can also be referenced
* by a pointer like any arguments passed on the stack).
*/
extern void * __builtin_va_start(va_list, ...);
#ifdef _CFRONT
#define __builtin_isfloat(a) __builtin_alignof(a)
#endif
#define va_start(list, v) __builtin_va_start(list, v, 1)
#define va_end(list)
#define va_arg(list, mode) \
( *( ((list).offset += ((int)sizeof(mode) + 7) & -8) , \
(mode *)((list).a0 + (list).offset - \
((__builtin_isfloat(mode) && (list).offset <= (6 * 8)) ? \
(6 * 8) + 8 : ((int)sizeof(mode) + 7) & -8) \
) \
) \
)
#elif defined(_M_PPC)
/* Microsoft C8 front end (used in Motorola Merged compiler) */
/* bytes that a type occupies in the argument list */
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
/* return 'ap' adjusted for type 't' in arglist */
#define _ALIGNIT(ap,t) \
((((int)(ap))+(sizeof(t)<8?3:7)) & (sizeof(t)<8?~3:~7))
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap = (char *) (_ALIGNIT(ap, t) +
_INTSIZEOF(t))) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
#elif defined(_M_M68K)
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + (sizeof(v) < sizeof(int) ? sizeof(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_MPPC)
#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 )
#else
/* A guess at the proper definitions for other platforms */
#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 )
#endif
///////////////////
Va_start宏计算了首个参数的指针地址,放到va_list中;va_arg计算了后面的参数的首地址,放到va_list中,从而可以找到下一个参数,第
二个参数是type类型,在宏中,用来计算类型的大小以确定下一个参数的指针地址;va_end实现了将va_list
指向自身类型的空指针,也就是初始化为空了。
了解了主要用到的宏的实现原理,我们计划一个小的例子来模拟这种不定数量参数的实现。
定义一个结构体人,在人这个结构体中,有姓名和年龄两个变量
Struct 人
{
姓名;
年龄;
};
//初始化一些“人”
人 好多人 []= {
“老赵”, 25,
“老钱”, 26,
“老孙”, 27,
“老李”, 28,
…….
};
//然后在定义一个函数“打印这些信息”
Void 打印这些信息(人 first,…)
{
//实现打印
}
有了这个思路:下面实现它:
#include <stdio.h>
#include <stdarg.h> //保存va_list等宏的头文件
struct Person
{
char* name; //姓名
unsigned int age; //年龄
};
void PrintInfo(Person first,...);//实现功能函数,打印信息
void main()
{
Person LotsOfPeople[] = {
"老赵",25,
"老钱",26,
"老孙",27,
"老李",28
};
Person Null = {"", 0};//结束条件
printf("测试开始...\n\n");
PrintInfo(Null);
PrintInfo(LotsOfPeople[0],Null);
PrintInfo(LotsOfPeople[0],LotsOfPeople[1],Null);
PrintInfo(LotsOfPeople[0],LotsOfPeople[1],LotsOfPeople[2],Null);
PrintInfo(LotsOfPeople[0],LotsOfPeople[1],LotsOfPeople[2],LotsOfPeople[3],Null);
printf("\n测试结束...\n按下任意键结束。。。");
getchar();
}
void PrintInfo(Person first,...)
{
printf("现在输出。。。\n");
Person i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( (i.name != "") && (i.age != 0) )
{
printf("名字为:%s\t年龄为:%d\n",i.name,i.age);
i = va_arg( marker, Person);
}
va_end( marker );
printf("\n");
}
我新加的示例:
#include <iostream.h>
#include <stdarg.h>
int sumNum(int begin,...);
void main()
{
int sum = sumNum(8,1,2,3,4,5,6,7,8,9,10);
cout<<sum<<endl;
}
int sumNum(int begin,...)
{
int sum = 0,iCount=begin,index = 1,i;
va_list maker;
va_start(maker,begin);
i = va_arg(maker,int);
while (index <= iCount)
{
index++;
sum+=i;
i = va_arg(maker,int);
}
va_end(maker);
return sum;
}