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

【日积月累】C/C++可变参数函数的实现

2013-09-28 22:56 513 查看
1、可变参函数的原理

C/C++函数的参数是存放在栈区的,并且参数的入栈是从参数的右边开始,即最后一个参数先入栈,而第一个参数最后才入栈,所以,根据栈的后进先出性质,函数总能找到第一个参数。所以,可变参函数的实现必须能够从已知参数中获取到函数所需要参数的个数;

例如printf函数,第一个参数就是一个格式串,而后面所需要的参数个数能够从格式串中得到。

2、可变参函数的设计

标准头文件stdarg.h提供了一套对可变参函数的实现机制,所以编写可变参函数需要包含该头文件。#include<stdarg.h>

C中变长实参头文件stdarg.h提供了一个数据类型va-list和三个宏(va-start、va-arg和va-end),用它们在被调用函数不知道参数个数和类型时对可变参数表进行测试,从而为访问可变参数提供了方便且有效的方法。va-list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va-list的变量,该变量用来指向va-arg和va-end所需信息的位置。

VC++中va_list、va_start、va_arg、va_end的细节:

va_list:

typedef char *  va_list;

va_start:

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

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


作用:va_start的第一个参数是va_list变量指针,第二个参数应该传可变参函数的最后一个固定形参,其作用是使va_list类型的指针指向变参函数的第一个可变形参.

va_arg:

#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )


作用:va_arg宏从上面的实现代码可以看出,其具有两个作用:第一是使va_list类型的指针指向下一个可变参数;第二是获取va_list类型指针指向的值.va_arg宏的第一个参数是va_list变量,第二个参数是待获取的参数的数据类型.

va_end:

#define _crt_va_end(ap)      ( ap = (va_list)0 )


作用:va_end主要是对资源释放,使va_list类型的指针不指向任何内存.

从上面对va_list、va_start、va_arg、va_end的分析可以知道,要编写可变长参数函数,我们首先要定义一个va_list类型的变量arg_ptr,然后使用va_start(arg_ptr,arg_list)宏使变量arg_ptr指向对一个可变形参,然后才能使用va_arg获取各个变参的值,在最后一定要使用va_end宏释放资源.

3、可变长参数函数的实现

分析:在上文的设计分析中,对于va_arg宏的使用有两个比较关键的点,第一个是可变形参的参数个数;第二就是可变形参的参数类型,因为va_arg要根据参数的类型寻址;若知道参数的个数和参数的类型,则可变参函数的实现较为简单;否则,我们要编写一些逻辑去判断实现.

实例1:实现一个可变参表的求和函数

int sum(int n, ...)
{
va_list arg_ptr = NULL;
int i = 0, nRes = 0;
va_start(arg_ptr, n);
for(; i < n; ++i)
{
int temp = va_arg(arg_ptr, int);
nRes += temp;
}
va_end(arg_ptr);
return nRes;
}

说明:该实例的实现比较简单,函数的固定参数表明函数参数的个数,其次,参数的类型固定为int型.

实例2:实现自己的printf函数

void myPrintf(char * strFormat, ...)
{
if(NULL == strFormat)
return;
char strInfo[1000] = {0};
va_list arg_ptr = NULL;
va_start(arg_ptr, strFormat);
vsprintf(strInfo, strFormat, arg_ptr);
va_end(arg_ptr);
fputs(strInfo, stdout);
}

说明:虽然函数参数的个数和参数是未知的,但是使用了vsprintf函数,实现的代码也比较简单,至于对vsprinf函数不了解的可以去baidu、google

完整的代码:

#include <iostream>
#include <stdarg.h>

int sum(int n, ...) { va_list arg_ptr = NULL; int i = 0, nRes = 0; va_start(arg_ptr, n); for(; i < n; ++i) { int temp = va_arg(arg_ptr, int); nRes += temp; } va_end(arg_ptr); return nRes; }

void myPrintf(char * strFormat, ...) { if(NULL == strFormat) return; char strInfo[1000] = {0}; va_list arg_ptr = NULL; va_start(arg_ptr, strFormat); vsprintf(strInfo, strFormat, arg_ptr); va_end(arg_ptr); fputs(strInfo, stdout); }

int main()
{
myPrintf("%d\n", sum(3, 45, 15, 45, 100));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: