C语言可变参数只会用算啥本事?看我来抽丝剥茧干翻它!

看山是山,看山不是山,最终看山才是山,并且是无穷的山峦。当我们学习一门技术的时候,起初是先模仿,但是最终是为了超越,也就是得到秘籍,看到本质。

于是,今天来继续看可变参数,我们来分析这个过程,代码如下:

声明int add (int num,...);...会说明是个可变函数,这样子编译器在编译遇到的函数的地方,就知道自动解析,依据传入的参数,直接进行进栈,从而不需要报错。

我们默认的函数,如果声明是两个参数,调用的是三个参数的话,最终会出错,会提示你找不到实现体。

声明完了后,实现函数,然后我们在使用的地方,直接传入多个参数,就不会出问题。

我们这里要看下add里面的具体实现,第一个参数我们利用这个值,用来判定长度,循环的结束点。

然后使用va_start 来卡找到第一个参数的地址,我这节画个图,大家就能理解了。再一个你也就明白了,为什么是int add(int num,...);而不是int add(...); 因为如果是这个,你实现的时候,就没法定位到起始地址,导致你无法解析后续的参数,这个明白了吧。

所以,编译时候,系统会提示错误,规定...参数前面必须有一个有名参数,否则系统编译的时候,你在实现体里面,无法做处理。这里就是 va_start(valist,num);

然后我们使用va_arg 去获取后续的参数,第一个是起始地址,第二个是后面紧跟着的数据,该以哪个大小去解析。我们这里是int,都是这个尺寸,所以用了循环。

在printf里面,使用的是%s ,%f这类处理,依据这个会变化。

使用完成后,就可以用va_end来结束指针。

我们如果把调用地方改成add(5,a,b);最终能输出出来结果,但是非常乱,原因很简单,这里的第一个参数5,让遍历寻找了栈上面的一些脏数据,导致结果未知。

我们如果把int b=6改成float b=6;会发生什么神奇的现象呢?出现了神奇的结果,原因很简单,float 进入栈的时候,占用的空间比int大,但是我们执行的时候,用了int大小去解析了这个数据,导致出现问题。

我们来看下如何修正这个问题,就需要格式化处理了,第一个参数,我们把它调整下,变成char *format,我们把代码改成这个,

为什么这里有float变为了double,主要是方便系统进行处理,升级后你 解析的时候,就要用double去解析,否则的话你处理完数据,ap指针就没有指到下一个位置,导致出错。

不过这个问题现在你不需要担心,如果你没有写对,系统会在编译时候提醒你,直接系统报错,让你去修改的。

这样子看下来,是不是觉得可变参数也没多神奇了?简单说下就是编译器支持...让函数参数可变,同时保留一个有名参数,让实现体可以用这个去定位到起始位置,然后进行遍历解析,完成逻辑处理。

我们把这个再抽象一层,简单来说,就是一组数据约束,存放在一起,然后我们依据一个格式化参数,对这个数据进行解析处理。当你看到这个的时候,就突然明白,协议的概念。

协议就是约定,约定双方同时遵循一个规则,同时遵守,就是协议。TCP/IP协议,ELF文件解析协议。

当你抽象到这里,基本上就大彻大悟,一切处理都是协议头 数据( 校验)。

在今天最后,我们来看下反编译后的代码,就明白了第一个参数的地址的意义,我这里用的int b=6;原因是如果是float的话,指令会比较复杂,不方便我们学习。

这里可以看到,我们地参数是 %d,%d  a b  这三个,在汇编代码中,可以看到,

rbp-0x8 放的是b的值,6

rbp-0xc 放的是a的值,5

rbp-0x14,放着一个地址4007ec,这地方就是%d,%d 常量字串位置

所以我们add函数实现里面,fmt拿到的就是rbp-0x14的地址,也就是第一个参数,随后的解析就是依据给的格式,把对应数据解析出来,移动指针ap的位置到合适地方(依据对齐原则,以及sizeof(数据类型)

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

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

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

相关文章

Linux下CMAKE编译jsoncpp,使用CMake引入jsoncpp

在jni的cpp中使用json,百度了一下,大多都是下面这个库,但是和之前的文档有点出入了,记录一下。jsoncpp库地址:添加jsoncpp库在cpp目录下创建一个jsoncpp目录下载jsoncpp项目把src\lib_json文件夹下的文件都拉到项目的c…

python打印星星居中_python实现while循环打印星星的四种形状

在控制台连续输出五行*,每一行星号数量一次递增***************#1.定义一个行计数器row 1while row < 5:#定义一个列计数器col 1#开始循环while col < row:print(*,end)col 1print()row 1如果想要星星倒过来呢#1.定义一个行计数器row 1while row < 5:#定义一个列…

java8升级java12_为什么现在是升级到Java 8的最佳时机

java8升级java12有兴趣了解如何通过AppDynamics充分利用Java 8的新功能吗&#xff1f; 立即开始免费试用 &#xff01; 今年3月&#xff0c;Oracle发布了近十年来最受期待的版本Java8。自发布以来&#xff0c;最新版本引起了越来越多的关注&#xff0c;各种规模的公司都渴望升…

C语言#include还有些你不知道的事

#include简介在C语言中#include是preprocessor的一条指令&#xff0c;告诉预处理器将指定头文件的内容插入到预处理器命令的相应位置。#include "xxx.h" 和 #include有两种方式可以指定插入头文件&#xff1a;#include #include "filename"如果需要包含标…

java常见的ide_在三个Java IDE中生成的三种常见方法

java常见的ide在本文中&#xff0c;我研究了NetBeans 8.0.2 &#xff0c; IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种“通用”方法[ equals&#xff08;Object&#xff09; &#xff0c; hashCode&#xff08;&#xff09;和toString&#xff08;&#xff09; ]的区别…

深度linux安装依赖,Linux -- Ubuntu下载deepin wine依赖问题笔记

问题开始下载deepin-wine安装包, 请稍后…1.1udis86_1.72-2_i3 100%[>] 34.18K 87.3KB/s 用时 0.4s1.2deepin-fonts-win 15%[> ] 31.18K 1.72KB/s 用时 18s1.2deepin-fonts-win 100%[>] 207.88K 26.2KB/s 用时 6.7s2.1deepin-libwine_2 100%[>] 18.97M 132KB/s 用时…

什么是C语言中的隐式函数声明?

「1、什么是C语言的隐式函数声明」在C语言中&#xff0c;函数在调用前不一定非要声明。如果没有声明&#xff0c;那么编译器会自动按照一种隐式声明的规则&#xff0c;为调用函数的C代码产生汇编代码。下面是一个例子&#xff1a;int main(int argc, char** argv) {double x a…

群晖 上传 源文件不存在_群晖NAS连接百度网盘报错?原因是这样的

群晖NAS附带的云同步套件可以与国内外多个网盘连接 , 连接后可从云上下载数据亦可从本地将数据上传到云上。例如通过云同步套件连接百度网盘账号后可以便捷上传和下载数据 , 若网盘空间较大甚至可用来备份整个NAS等。不过现在看来群晖与百度网盘的合作似乎已经结束&#xff0c;…

ssl/tls服务器瞬时_SSL / TLS REST服务器–带有Spring和TomEE的客户端

ssl/tls服务器瞬时在构建系统时&#xff0c;开发人员通常会忽略安全性方面。 安全一直是令人担忧的重要问题&#xff0c;但是它比以前吸引了更高的关注。 就在今年&#xff0c;我们发生了像Heartbleed Bug或CelebrityGate丑闻这样的案件。 这与帖子无关&#xff0c;只是安全真正…

linux kvm百度云,容器与云|如何在 Ubuntu Linux 上使用 KVM 云镜像

如何下载并使用运行在 Ubuntu Linux 服务器上的 KVM 云镜像&#xff1f;如何在 Ubuntu Linux 16.04 LTS 服务器上无需完整安装即可创建虚拟机&#xff1f;如何在 Ubuntu Linux 上使用 KVM 云镜像&#xff1f;基于内核的虚拟机(KVM)是 Linux 内核的虚拟化模块&#xff0c;可将其…

C 的16个大坑,你能躲过几个?

首先说下C 和C语言有什么区别&#xff1f;分享一个我在知乎上看见的回答&#xff1a;C ≈ C with classes&#xff0c; C with STLC&#xff1a;面向机器编程C &#xff1a;面向编译器编程C 有个很重要的特性叫RAII&#xff0c;个人认为可以多多使用&#xff0c;相当方便。言归…

python占位符怎么输入_python占位符怎么输入

占位符&#xff0c;顾名思义就是插在输出里站位的符号。占位符是绝大部分编程语言都存在的语法&#xff0c; 而且大部分都是相通的&#xff0c; 它是一种非常常用的字符串格式化的方式。1、常用占位符的含义s : 获取传入对象的__str__方法的返回值&#xff0c;并将其格式化到指…

java 性能调优_Java性能调优调查结果(第三部分)

java 性能调优这是本系列文章的第三篇&#xff0c;我们将分析2014年10月进行的调查的结果。如果您尚未这样做&#xff0c;我建议从本系列的前两篇文章开始&#xff1a; 问题严重性分析和监视域分析 。 这篇文章着重于故障排除/根本原因检测。 本调查部分的背景&#xff1a;意识…

不懂指针类型,7个例子给你讲明白

1. int va;这是一个整型变量&#xff0c;32位CPU的话&#xff0c;占有32个bite2. int *va;这是一个整型指针变量&#xff0c;用于存放一个整型变量的地址&#xff0c;3. int **va;这是一个整型的二级指针&#xff0c;用于存放一个内存的地址&#xff0c;该地址对应的内存中存放…

linux ffmpeg yum源,ffmpeg最新的yum源地址及视频去logo

一&#xff1a;ffmpeg 最新yum源cat /etc/yum.repo.d/atrpms.repo[atrpms]nameRed Hat Enterprise Linux $releasever - $basearch - ATrpmsfailovermethodprioritybaseurlhttp://dl.atrpms.net/el$releasever-$basearch/atrpms/stableenabled1gpgcheck0gpgkeyfile:///etc/pki/…

Tomcat与Netty比较

Tomcat介绍Tomcat支持的协议Tomcat的优缺点Netty介绍Netty支持的协议Netty的优点和缺点Tomcat和Netty的区别Tomcat和Netty的应用场Tomcat和Netty来处理大规模并发连接的优化Tomcat与Netty的网络模型的区别Tomcat与Netty架构设计拓展 Tomcat介绍 Tomcat是一个免费的、开放源代码…

spring 项目集成配置_Spring重试–与项目集成的方式

spring 项目集成配置如果您需要在代码中实现健壮的重试逻辑&#xff0c;一种行之有效的方法是使用spring重试库。 我的目的不是要展示如何使用spring retry项目本身&#xff0c;而是要演示将其集成到代码库中的不同方式。 考虑一种服务来调用外部系统&#xff1a; package re…

redis 内存不足 排查_排查redis占用内存达90%以上

帮别人排查一个问题,项目还没上线但redis占用内存很高。思路如下&#xff1a;1、登陆redis控制台&#xff0c;首先用 keys * 获取所有的key> keys *x:x:ax:x:bx:x:c发现key也就十来个&#xff0c;用 TYPE x:x:a 发现a是一个list数据类型用lrange命令查看list中指定索引的值用…

C或C 如何通过程序执行shell命令并获取命令执行结果?

1 应用场景最近在实际程序开发中&#xff0c;需要通过程序执行 shell 命令&#xff0c;并获取命令输出内容。但是系统自带的 system 只能返回命令执行成功与否&#xff0c;不能捕获命令输出。2 扩展性由于应用场景本就广泛&#xff0c;因此扩展性较好。此函数可以执行任意命令&…

linux centos7安装ngix,centos7 环境下安装nginx--Linux

本文将要为您介绍的是centos7 环境下安装nginx--Linux,具体完成步骤:一、安装前需要的编译环境准备1、安装makeyum install -y gcc automake autoconf libtool make2、安装gcc、gcc-cyum install -y gcc gcc-c3、关闭防火墙iptables -F4、关闭selinux#临时关闭&#xff1a;sete…