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

1、可变参函数的原理 ios

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

例如printf函数,第一个参数就是一个格式串,然后面所须要的参数个数可以从格式串中获得。 测试

2、可变参函数的设计 google

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

C中变长实参头文件stdarg.h提供了一个数据类型va-list和三个宏(va-startva-argva-end),用它们在被调用函数不知道参数个数和类型时对可变参数表进行测试,从而为访问可变参数提供了方便且有效的方法。va-list是一个char类型的指针,当被调用函数使用一个可变参数时,它声明一个类型为va-list的变量,该变量用来指向va-argva-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类型的指针指向变参函数的第一个可变形参. code

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

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

  • 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宏释放资源.

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

分析:在上文的设计分析中,对于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;
}
相关文章
相关标签/搜索