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);
- }
http://blog.csdn.net/rabinsong/article/details/8946514
C++可变参数的另一种实现的更多相关文章
- c 可变参数 定义可变参数的函数
定义可变参数的函数,需要在stdarg.h头文件中定义的va_list类型和va_start.va_arg.va_end三个宏. 定义可变参数函数 va_list ap; //实际是定义一个指针va ...
- 理解 Python 中的可变参数 *args 和 **kwargs:
默认参数: Python是支持可变参数的,最简单的方法莫过于使用默认参数,例如: def getSum(x,y=5): print "x:", x print "y:& ...
- Java 传递可变参数和方法重载
形式:类型... 参数名 示例:public void show(int... a) {}; 可变参数在方法中被当作数组来处理 可变参数传值的四种方式: 一个值也不传,可变参数会接收到长度为0的数组 ...
- define可变参数,float数据传输
define可变参数 一般在调试打印Debug信息的时候, 需要可变参数的宏. 从C99开始可以使编译器标准支持可变参数宏(variadic macros), 另外GCC也支持可变参数宏, 但是两种在 ...
- [11 Go语言基础-可变参数函数]
[11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...
- 嵌入式C语言自我修养 12:有一种宏,叫可变参数宏
12.1 什么是可变参数宏 在上面的教程中,我们学会了变参函数的定义和使用,基本套路就是使用 va_list.va_start.va_end 等宏,去解析那些可变参数列表我们找到这些参数的存储地址后, ...
- 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法
具体见第三阶段scala-day01中的文档(scala编程基础---基础语法) 1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...
- C可变参数的函数
我们实现一个简单的printf函数(可变参数) #include <stdio.h> #include <stdarg.h> void myprintf(const char ...
- 可变参数列表与printf()函数的实现
问题 当我们刚开始学习C语言的时候,就接触到printf()函数,可是当时"道行"不深或许不够细心留意,又或者我们理所当然地认为库函数规定这样就是这样,没有发现这个函数与普通的函数 ...
随机推荐
- 关于新装ubuntu系统update失败和build-essential失败的解决办法
我是12月4日在新电脑上的vmware-workstation 10 上安装的ubuntu14.04LTS,但安装后再校园环境下总是build-essential失败,上网一查,说是要先update, ...
- PHP比较全的友好的时间显示,比如‘刚刚’,'几秒前'等
分享一个php友好的比较完成的时间格式化函数,包括‘刚刚’,'几秒之前',‘几分钟前’,'几小时前',几天前,几周前,几个月前等.调用方式很简单,是从ThinkSNS 里面拿出来的. /** * 友好 ...
- jQuery中的类型判断
在JQuery中有一个type方法,在1.11.2中是这样写的 var class2type = {}; var toString = class2type.toString; jQuery.each ...
- 判断浏览器及设备的打开方式,自动跳转app中
如果安装了APP则自动条状app,如果没安装则自动跳转下载页面 <head> 放在head中加载 <script> function redirect() { var appU ...
- codeforces 645C . Enduring Exodus 三分
题目链接 我们将所有为0的位置的下标存起来. 然后我们枚举左端点i, 那么i+k就是右端点. 然后我们三分John的位置, 找到下标为i时的最小值. 复杂度 $ O(nlogn) $ #include ...
- Myeclipse普通工程转为Maven工程
在SVN导出的Maven项目,或以前不是用Maven管理的项目想要转换成Maven项目,但Myeclipse中右键Configure 找不到(eclipse可行)Convert to maven pr ...
- Linux常用命令--网络管理篇(三)
ping –b 10.0.0.255 扫描子网网段 ifconfig 查看网络信息 netconfig 配置网络,配置网络后用service network restart重新启动网络 ifconfi ...
- 升级linux内核(2.6.32->3.10.81),安装docker
1.内核升级环境准备 #查看已经安装的和未安装的软件包组,来判断我们是否安装了相应的开发环境和开发库: yum grouplist #一般是安装这两个软件包组,这样做会确定你拥有编译时所需的一切工具 ...
- CSS转载备忘
原文地址:http://www.cnblogs.com/coffeedeveloper/p/3145790.html#html 转载内容: 对CSS中的Position.Float属性的一些深入探讨 ...
- BadgeView使用介绍
前段时间做的一个淘宝客的项目,需要在商品图片上添加价格标签,之前自己使用TextView和Cavas绘制的感觉效果一般,今天偶然在CSDN上发现BadgeView这个开源项目,在git下载下来之后,使 ...