C/C++ 日常学习总结(第十八篇)参数个数可变的函数

【发生前提】:

               这个参数可变的函数一般情况下,我们不怎么会遇到,往往在程序中会把参数个数写死,但是如设置输入输出时会要求参数个数可变,比如:printf(),scanf(),如果想实现自己的printf函数,那么就需要了解其原理。

 

【函数本质】:

              函数参数在内存中是连续存放的。

 

【基础了解】:

                             c/c++编译器采用宏的形式支持可变参数函数。这些宏包括va_start、va_arg和va_end等。之所以这么做,是为了增加程序的可移植性。屏蔽不同的硬件平台造成的差异。支持可变参数函数的所有宏都定义在stdarg.h 和 varargs.h中。

                            使用宏_INTSIZEOF是为了按照整数字节对齐指针,因为c调用协议下面,参数入栈都是整数字节(指针或者值)。

(1.)

#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end


(2.)

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#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 )


 

【实现举例】:

 (1.)参数类型是整形int,这种比较容易

 

int Average1(int first, ...)
{
	int count = 0;
	int sum = 0;
	int *p = &first;

	while (*p != -1)
	{
		sum += *(p++);
		count++;
	}
	return( sum ? (sum / count) : 0 );
}


 

printf( "2, 3, 4之和的平均值 = %d\n", Average1(2, 3, 4, -1));	//最后一个参数-1表示参数结束, 不参与计算
printf( "11, 2, 3, 4之和的平均值 = %d\n\n", Average1(11, 2, 3, 4, -1));	//最后一个参数-1表示参数结束, 不参与计算

 

(2.)参数类型是字符串类型,注意下面的例子没有使用第一个参数

int demo( char *msg, … )
 
{
 
  va_list argp; /* 定义保存函数参数的结构 */
 
  int argno = 0; /* 纪录参数个数 */
 
  char *para; /* 存放取出的字符串参数 */
 
   // 使用宏va_start, 使argp指向传入的第一个可选参数,
 
   // 注意 msg是参数表中最后一个确定的参数,并非参数表中第一个参数
 
   va_start( argp, msg );
 
   while (1)
 
   {
 
     //取出当前的参数,类型为char *
 
     //如果不给出正确的类型,将得到错误的参数
 
     para = va_arg( argp, char *);
 
     if ( strcmp( para, “\0″) == 0 ) /* 采用空串指示参数输入结束 */
 
         break;
 
     printf(”参数 #%d 是: %s\n”, argno, para);
 
    argno++;
 
   }
 
  va_end( argp ); /* 将argp置为NULL */
 
  return 0;
 
}
 
//输出结果
 
参数 #0 是: This
 
参数 #1 是: is
 
参数 #2 是: a
 
参数 #3 是: demo!


 

void main( void )
 
{
 
  demo(”DEMO”, “This”, “is”, “a”, “demo!”, “\0″);
 
}


(3.)在(2)中如何使第一个参数打印出来,为什么打印结果里面没有?那么这就需要去了解上面定义的宏的意义了。

va_start( argp, msg );

#define va_start _crt_va_start

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

第一句:是调用的

第二句:va_start是_crt_va_start的另一种表现形式

第三句:是_crt_va_start的原型,意思是ap指向的地址是该字符串的首地址+字符串的大小,也就是指向的是第二个字符串的首地址。

 

修改:

argp = (va_list)_ADDRESSOF(msg);

指向的便是第一个字符串。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值