您的位置:首页 > 编程语言 > C语言/C++

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

2014-09-03 14:11 423 查看
【发生前提】:

这个参数可变的函数一般情况下,我们不怎么会遇到,往往在程序中会把参数个数写死,但是如设置输入输出时会要求参数个数可变,比如: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);

指向的便是第一个字符串。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: