曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 2921|回复: 0
打印 上一主题 下一主题

va_arg, va_end, va_start

[复制链接]

35

主题

36

帖子

1969

积分

版主

Rank: 7Rank: 7Rank: 7

积分
1969
跳转到指定楼层
楼主
发表于 2011-9-5 01:35:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有些函数的参数个数是不确定个数的,比如如下声明的函数:
[C++] 纯文本查看 复制代码
void testit ( int i, ...);

int average( int first, ... );

DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...);

这几个函数都有几个共同的特征,参数列表的最后都有 '...',这表示不确定个数的参数列表。

va_arg, va_end, va_start 这几个函数宏正是用来处理上面所述情况。它们的声明如下:
[C++] 纯文本查看 复制代码
type va_arg(
   va_list arg_ptr,
   type 
);

void va_end(
   va_list arg_ptr 
);

void va_start(
   va_list arg_ptr,
   prev_param 
); // (ANSI version)

void va_start(
   arg_ptr 
);  // (Pre-ANSI-standardization version)


va_list 代表的就是 '...' 省略号部分的参数,它实际上是一个字符指针,在使用 va_start , va_arg 之前,一般都需要定义一个 va_list 的变量,以能在后续中操作这些可选参数。

va_start 用来进行选项参数的初始化,它的第 1 个参数就是事先定义的 va_list 类型变量,第 2 个参数是 '...' 之前的那个参数,比如对于 void testit ( int i, ...); 这个函数,va_start 中的第 2 个参数设为 i;又比如 Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...); 这个函数,在设置 va_start 时,第 2 个参数就是 OptStr 。

如果对所有的可选参数都处理完了,那么就用 va_end 进行一下善后工作,它所做的工作就是使上面的 va_list 定义的可选参数指针置为空。它只有一个参数,就是开始时定义的 va_list 变量。

最重要的一个就是 va_arg ,它负责处理可选参数。它处理这些参数的一般方法是使用一个循环遍历所有的可选参数项,每一次循环就会使可选参数指针指向下一个参数。

下面是一个简单示例(代码来自MSDN,这里不用循环遍历可选参数列表)
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdarg.h>

void testit ( int i, ...)
{
   va_list argptr;
   va_start(argptr, i);

   if ( i == 0 ) {
      int n = va_arg( argptr, int );
      printf( "%d\n", n );
   } else {
      char *s = va_arg( argptr, char* );
      printf( "%s\n", s);
   }
}

int main()
{
   testit( 0, 0xFFFFFFFF ); // 这里有问题,第 2 个参数 0xffffffff 不是 int 型
   testit( 1, NULL );       // 这里也有问题, 第 2 个参数 NULL 不是字符指针类型
}

运行输出:
-1
(null)
上面代码里,两次运用 testit() 都有问题。第 1 次使用的 testit() 函数的第 2 个参数是个 unsigned int 型,而不是 int 型,这样就和  va_arg( argptr, int ); 里所声明的不一致。第 2 次使用的 testit() 时,第 2 个参数 NULL 实际上十个 int 型,而非 char * 型,这和 va_arg( argptr, char* ); 里声明的也不一致。虽然这样,但并不会给程序运行带来错误,因为它们在传进 testit() 时会自动被强制转换。然而,在某些编译选项限制下,就会产生异常,并报告类型不匹配。所以,严格的来说,在 testit() 时所传递的第 2 个参数要和函数体内 va_arg() 里说明的一致,所以对于上面的两个问题,可在 main() 中强制转换 0xffffffff 为 int 型,如 (int)0xffffffff ,以及强制转换 NULL 为 char * 型,如 (char *) NULL 。

示例2
[C++] 纯文本查看 复制代码
#include "stdafx.h"
int average( int first, ... );


int _tmain(int argc, _TCHAR* argv[])
{
      /* 可选参数里使用 3 个整数作为参数,-1 作为结束符号 */
   printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );

   /* 4  个整数作为可选参数 */
   printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );

   /* 只有 -1 一个可选参数 */
   printf( "Average is: %d\n", average( -1 ) );

    return 0;
}

/* 返回可选参数列表里所有整数参数的平均数 */
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /*初始化变量参数 */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* 重置变量参数     */
   return( sum ? (sum / count) : 0 );
}

运行输出:
Average is: 3
Average is: 8
Average is: 0
本人冰火岛大学毕业,获武学奇才与医学精英双硕士学位,精通《九阳神功》,《乾坤大挪移》,《七伤拳》,《太极拳》以及各种医术毒经;熟练掌握《咯吱功》(专咯吱敏敏)。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-18 23:00 , Processed in 0.077112 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表