【C++】变参函数va_start,va_arg,va_end介绍及实现方式

news2024/11/20 1:47:00

如果写过JS的话,就知道在JS中定义一个函数,就算输入的实参和形参不一致,也可以同过arguments获取参数

function abc(x)
{
	console.log(x)
    console.log(arguments[0])
    console.log(arguments[1])
    console.log(arguments[2])
}
abc(1,2,3)

上例输出

1
1
2
3

在C/C++中其实也可以通过一些方式,获取实参中没有定义的多余参数,被称作变参函数

以下代码在32位 vs2015 win10中调试通过

变参函数

在C/C++定义一个变参函数需要使用三个点…,定义形参

void abc(...)

然后实参的话,就可以随便传入任何类型和数量的参数

abc(3,4,"aaa");

要在被调用的函数中取得参数值的话,在C/C++中有专们的函数来处理接收实参,一般情况下需要三个宏和一个类型来配合使用

基本使用如下

void abc(int i,...)
{
    va_list argptr;
    va_start(argptr, i);
    int aaa = va_arg(argptr, int);
    char * bbb = va_arg(argptr, char*);
    va_end(argptr);
    printf("i:%d,aaa:%d,bbb:%s",i,aaa,bbb);
}

int main() {
    
    abc(3,4,"aaa");
    return 1;
}

以上代码输出

i:3,aaa:4,bbb:aaa

宏定义说明如下:

va_list:定义了使用va_start,va_arg,va_end所需要的一些信息
va_start:表示的是设置头一个参数,它的第一个参数是va_list 当以的变量,第二个参数是函数形参的变量名
va_arg:表示从第二个参数开始获取实参,它的第一个参数是va_list定义的变量,第二个参数是函数第二个参数的类型名称,每调用一次va_arg,就获取下一个参数
va_end:表示清理va_list定义

使用时的整个流程如下

1.定义va_list
2.将第一步定义的va_list,传入va_start,获取函数参数的第一个
3.反复调用va_arg,获取变参函数未定义在形参内的变量
4.用va_end清理va_list

整个使用方式流程并不复杂

变参函数的实现原理

实际上实现上述操作,并不复杂,只要知道函数是如何调用的,参数是如何传入的,如何被存储的就可以。

调用一个函数,如下例

void abc(int a,int b)
{
	printf("%d,%d", a, b);
}
int main() {	
	abc(1, 2);
	return 1;
}

执行这个函数时,首页要从最后一个参数2,开始把参数都压入栈内,然后执行call调用abc

push 2  
push 1  
call abc
add  esp,8 

在执行到abc内以后,存储在栈内参数如下
在这里插入图片描述
可以看到参数都是排列在一起的,所以只要知道第一个参数地址,后面的值,按照他所占用空间大小就可以拿出来

自己实现va_start,va_arg,va_end

按上述描述,事实上参数都是挨着的,只要知道第一个参数的地址,后面就都能知道,所以可以模仿也写一个

代码如下

#define my_va_start(a,b) a =(char*)&b
#define my_va_arg(a,b)  *((b*)(a = a + sizeof(b)))
#define my_va_end(a) a = 0
void abc(int i, ...)
{

	char* my_wa_list;
	my_va_start(my_wa_list, i);
	int aaa = my_va_arg(my_wa_list, int);
	char * bbb = my_va_arg(my_wa_list, char*);
	my_va_end(my_wa_list);
	printf("i:%d,aaa:%d,bbb:%s", i, aaa, bbb);
}
int main() {
    
    abc(3,4,"aaa");
    return 1;
}

以上代码输出

i:3,aaa:4,bbb:aaa

用更简单的方法实现

在一个函数被call的时候,实际上,参数存在的位置前两已经存在两个值
在这里插入图片描述
第一个框:记录了在调用函数时,调用函数EBP的内容
第二个框:记录了被调用函数执行完毕后,要返回的代码地址,也就是执行完call abc后要执行的下一句地址

然后,后面就是参数了

所以取值时候,只需要取到EBP+8以后的两个值,就是参数的两个值,所以,函数即使没有形参,也可以取值

知道取值方式,实际上都不需要函数的第一个参数的地址,就可以取出,下面用内嵌asm实现

void abc(...)
{	
	int a, b;
	char* c;
	_asm {
		mov eax,[ebp+8]
		mov a,eax
		mov eax,[ebp+12]
		mov b,eax
		mov eax,[ebp+16]
		mov c,eax
	}	
	printf("a:%d,b:%d,c:%s", a, b, c);
}

int main() {
	abc(3, 4, "aaa");
	return 1;
}

以上代码输出

a:3,b:4,c:aaa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1137985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法通关村第三关-青铜挑战数组专题

本期大纲 线性表基础线性表概念数组概念 数组的基本操作数组创建和初始化查找一个元素增加一个元素修改一个元素删除一个元素 小题一道 - - 单调数组问题小题一道 - - 数组合并问题小结 线性表基础 线性表概念 我们先搞清楚几个基本概念,在很多地方会看到线性结构…

Adversarial attacks and defenses on AI in medical imaging informatics: A survey

Adversarial attacks and defenses on AI in medical imaging informatics: A survey----《AI在医学影像信息学中的对抗性攻击与防御:综述》 背景: 之前的研究表明,人们对医疗DNN及其易受对抗性攻击的脆弱性一直存在疑虑。 摘要:…

实力认可!安全狗入选2023年国产云原生安全技术代表厂商

10月25日,安全牛发布了《2023云原生安全技术应用指南》报告暨年度代表性厂商推荐。 作为国内云原生安全领导厂商,安全狗凭借突出的云原生安全产品与实力成为2023年国产云原生安全技术代表厂商之一。 厦门服云信息科技有限公司(品牌名&#xf…

04MQ消息队列

一、同步异步通讯 1.同步通讯和异步通讯 同步通讯:当A服务调用B服务时,一直等待B服务执行完毕,A才继续往下走 优点:时效性强,可以立即得到结果。 缺点: ①耦合度高,加入新的需求就要修改原…

计算机网络【CN】TCP报文段格式【20B】

序号:本报文段所发送的数据的第一个字节的序号确认号:期望收到对方下一个报文段的第一个数据字节的序号。 重要控制位: 紧急位URG:URG1时,标明此报文段中有紧急数据,是高优先级的数据,应尽快传送…

卡巴斯基2009杀毒软件

下载地址:https://user.qzone.qq.com/512526231/main https://user.qzone.qq.com/3503787372/main

冯诺依曼体系结构与操作系统

文章目录 1. 冯诺依曼体系结构概念理解冯诺依曼体系结构的优势 2. 操作系统概念设计OS的目的和定位如何理解管理 1. 冯诺依曼体系结构 概念 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。 对于各个部…

java--跳转关键字和随机数

1.跳转关键字 break:跳出并结束当前所在循环的执行 continue:用于跳出当前循环的当次执行,直接进入循环的下一次执行 2.注意事项 break:只能用于结束所在循环,或结束所在switch语句的执行 continue:只能…

工业交换机常用功能有哪些?

工业交换机,又称工业以太网交换机,是一种在OSI参考模型的第二层(数据链路层)上工作的网络设备。它基于MAC地址识别,并能够封装和转发数据包。那么,工业交换机的常见常用功能有哪些呢? 工业交换…

JavaWeb——关于servlet种mapping地址映射的一些问题

6、Servlet 6.4、Mapping问题 一个Servlet可以指定一个映射路径 <servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern> </servlet-mapping>一个Servlet可以指定多个映射路径 <servlet-mapping>&…

Redis和Memcached网络模型详解

1. Redis单线程单Reactor网络模型 1.1 redis单线程里不能执行十分耗时的流程&#xff0c;不然会客户端响应不及时 解决方法一&#xff1a; beforesleep里删除过期键操作若存在大量过期键时&#xff0c;会耗费大量时间&#xff0c;redis采用的策略之一就是采用timelimit方案超过…

2011-2021年“第四期”数字普惠金融与上市公司匹配(根据城市匹配)/上市公司数字普惠金融指数匹配数据

2011-2021年“第四期”数字普惠金融与上市公司匹配&#xff08;根据城市匹配&#xff09;/上市公司数字普惠金融指数匹配数据 1、时间&#xff1a;2011-2021年 指标&#xff1a;指标&#xff1a;股票代码、年份、行政区划代码、行业名称、行业代码、所属省份、所属城市、数字…

澳大利亚专线的清关时效

随着全球贸易的不断发展&#xff0c;越来越多的企业和个人开始选择通过海运、空运等物流方式将货物运送到世界各地。而在这些运输方式中&#xff0c;澳大利亚专线以其快速、高效的特性受到了广大客户的青睐。本文将为您详细介绍澳大利亚专线的清关时效。 一、澳大利亚专线的定义…

Vben开源添加本地路由(不用显示在菜单的路由)

1.在src\router\routes\modules下创建一个article.ts import type { AppRouteModule } from //router/types;import { LAYOUT } from //router/constant; import { t } from //hooks/web/useI18n;export const article: AppRouteModule {path: /article,name: Article,compon…

疫情集中隔离

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

数字人成品牌营销流量密码?看数字人花样赋能品牌

在第十二届李曼中国养猪大会暨世界猪业博览会上&#xff0c;嘉吉动物营养推出了首个数字人产品星推官&#xff0c;在现场数字人结合动捕设备&#xff0c;向观众实时介绍品牌的全明星产品&#xff0c;同时与观众进行实时互动&#xff0c;吸引了众多参展观众的驻足&#xff0c;为…

K8s安装部署-----二进制安装部署

目录 前言 一、操作系统初始化配置 二、部署 etcd 集群 1、在master01节点操作 2、在 node01 节点上操作 3、在 node02 节点上操作 4、查看集群状态 二、部署docker引擎 三、部署 Master 组件 1、准备证书 2、准备二进制文件、token 3、启动kube-apiserver服务 4、…

0基础学习PyFlink——用户自定义函数之UDTF

大纲 表值函数完整代码 在《0基础学习PyFlink——用户自定义函数之UDF》中&#xff0c;我们讲解了UDF。本节我们将讲解表值函数——UDTF 表值函数 我们对比下UDF和UDTF def udf(f: Union[Callable, ScalarFunction, Type] None,input_types: Union[List[DataType], DataTy…

macOS Sonoma 14.1正式版发布 改善Apple Music界面 新增保修状态显示

10月26日消息&#xff0c;苹果今天为 macOS Sonoma 推出了 14.1 版本更新&#xff0c;本更新主要改善了 Apple Music 界面&#xff0c;设置中新增保修状态&#xff0c;并修复了多项错误内容。 经过几周的用户测试&#xff0c;Apple 正式向所有 Mac 用户发布了 macOS Sonoma 14.…

【代码随想录01】数组总结

抄去吧&#xff0c;保存去吧&#xff01;