C++可变参数的另一种实现

大家熟知的C库函数printf函数就是一个可变参数函数,它是怎么实现的呢?不过他实现是有条件的,必须函数参数的入栈顺序为从右向左的顺序,也即函数的形参,在函数调用之前,必须是最右边的参数先入栈,并且参数都必须通过栈传递,以1个例子说明,如函数func(arg1, arg2,arg3),那么函数的堆栈应是:

                              

ebp是帧指针寄存器,一般用来存取堆栈,有了堆栈结构,下面我们看看C可变参数的具体实现原理:

#include <stdio.h>
enum {
		ptChar,
		ptInt,
		ptFloat,
		ptDouble,
	};
void printSum(unsigned long paramFormat, ...)
{
	/*高16位为可变参数类型,低16位为可变参数个数*/
	int paramType = (paramFormat >> 16);
	int paramNum = paramFormat & 0xffff;
	
	/*¶mFormat = ebp + 8,第一个参数的地址*/
	unsigned long *pArg = ¶mFormat;
	/*ebp + 0x0c, 第二个参数地址*/
	pArg++;
	switch(paramType)
	{
	case ptChar:
		{
			int sum = 0;
			for (int i = 0; i < paramNum; i++)
			{
				char *pValue = (char *)pArg;
				sum += *pValue;
				pArg++;
			}
			printf("%d\n", sum);
		}
		break;
	case ptInt:
		{
			int sum = 0;
			for (int i = 0; i < paramNum; i++)
			{
				int *pValue = (int *)pArg;
				sum += *pValue;
				pArg++;
			}
			printf("%d\n", sum);
		}
		break;
	case ptFloat:
		{
			float sum = 0;
			/**/
			pArg++;

			/*浮点参数,堆栈占8个字节,所以指针偏移为8*/
			for (int i = 0; i < paramNum; i++)
			{
				float *pValue = (float *)pArg;
				sum += *pValue;
				pArg++;
				pArg++;
			}
			printf("%f\n", sum);
		}
		break;
		case ptDouble:
		{
			double sum = 0;
			/*双精度浮点参数,堆栈占8个字节,所以指针偏移为8*/
			for (int i = 0; i < paramNum; i++)
			{
				double *pValue = (double *)pArg;
				sum += *pValue;
				pArg++;
				pArg++;
			}
			printf("%f\n", sum);
		}
		break;
	default:
		printf("unknowned type!\n");
		break;
	}
}

void main()
{
	unsigned long paramFormat = 3;
	char a = 1, b = 2, c = 3;
	printSum(paramFormat, a, b, c);


	paramFormat = ptInt << 16;
	paramFormat += 3;
	int ia = 1, ib = 2, ic = 3;
	printSum(paramFormat, ia, ib, ic);

	paramFormat = ptFloat << 16;
	paramFormat += 3;
	float fa = 1, fb = 2, fc = 3;
	printSum(paramFormat, fa, fb, fc);

	paramFormat = ptDouble << 16;
	paramFormat += 3;
	double da = 1, db = 2, dc = 3;
	printSum(paramFormat, da, db, dc);
}

上面这种方法对函数参数的入栈顺序有限制,必须从右向左入栈,这就是为什么pascal调用方式不能实现printf的原因,并且函数形参都要通过栈来传递,这对有些编译器为了优化处理,函数参数通过寄存器来传递,从而不满足要求。鉴于次,本文采用C++的默认形参实现可变参数的方法,没有上面的这些限制,下面是实现代码:

#include <stdio.h>
enum {
		ptChar,
		ptInt,
		ptFloat,
		ptDouble,
	};
void printSum(unsigned long paramType,
			  void *arg1 = NULL,
			  void *arg2 = NULL,
			  void *arg3 = NULL,
			  void *arg4 = NULL,
			  void *arg5 = NULL,
			  void *arg6 = NULL,
			  void *arg7 = NULL,
			  void *arg8 = NULL,
			  void *arg9 = NULL,
			  void *arg10 = NULL)
{
	void *arg[10] = {
		arg1,
		arg2,
		arg3,
		arg4,
		arg5,
		arg6,
		arg7,
		arg8,
		arg9,
		arg10,
	};
	switch(paramType)
	{
	case ptChar:
		{
			int sum = 0;
			for (int i = 0; i < 10; i++)
			{
				if (arg[i] != NULL)
				{
					char *pValue = (char *)arg[i];
					sum += *pValue;
				}
				else
					break;
			}
			printf("%d\n", sum);
		}
		break;
	case ptInt:
		{
			int sum = 0;
			for (int i = 0; i < 10; i++)
			{
				if (arg[i] != NULL)
				{
					int *pValue = (int *)arg[i];
					sum += *pValue;
				}
				else
					break;
			}
			printf("%d\n", sum);
		}
		break;
	case ptFloat:
		{
			float sum = 0;
			for (int i = 0; i < 10; i++)
			{
				if (arg[i] != NULL)
				{
					float *pValue = (float *)arg[i];
					sum += *pValue;
				}
				else
					break;
			}
			printf("%f\n", sum);
		}
		break;
		case ptDouble:
		{
			double sum = 0;
			for (int i = 0; i < 10; i++)
			{
				if (arg[i] != NULL)
				{
					double *pValue = (double *)arg[i];
					sum += *pValue;
				}
				else
					break;
			}
			printf("%f\n", sum);
		}
		break;
	default:
		printf("unknowned type!\n");
		break;
	}
}

void main()
{
	unsigned long paramType = ptChar;
	char a = 1, b = 2, c = 3;
	printSum(paramType, &a, &b, &c);

	paramType = ptInt;
	int ia = 1, ib = 2, ic = 3;
	printSum(paramType, &ia, &ib, &ic);

	paramType = ptFloat;
	float fa = 1, fb = 2, fc = 3;
	printSum(paramType, &fa, &fb, &fc);

	paramType = ptDouble;
	double da = 1, db = 2, dc = 3;
	printSum(paramType, &da, &db, &dc);
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RabinSong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值