可变参数函数的调用约定

如何解决可变参数函数的调用约定

初始化可变参数列表时,使用宏va_start并传递list_name,然后传递the last fixed parameter before the va list starts,因为“最后一个固定参数与第一个变量n相邻” 不知何故,这有助于识别堆栈中的var arg长度/位置(我之所以说是因为我不明白如何)。

使用cdecl调用约定(即从righ到左推入堆栈参数)the last fixed parameter before the va list starts在识别列表长度方面有什么用?例如,如果该参数是一个整数3,而变量参数也有一个3,那么被叫方将如何知道可变参数列表不止于此,因为还有另一个3 (固定参数),应该在那里结束?例如f(int a,int b,... )-> 通话 f(1,3,1,2,3)

反之亦然,有 guardian “样式”,您可以在调用函数时在可变参数的末尾添加NULL指针。再说一遍:如果NULL首先被推入栈,那将如何有用?难道不应该在参数的固定部分和可变部分之间插入NULL吗? (例如f(int a,... )-> 通话 f(a,b,NULL,param1,param2)

解决方法

如果我正确理解了您的疑问,那么您基本上要问的是:如果所有参数都在没有附加信息的情况下被推入堆栈,可变参数函数如何计算可变参数的起始位置?

您已经注意到,参数以相反的声明顺序推入堆栈:这意味着称为void f(int a,...)的{​​{1}}先推f(1,2,3),然后推3 ,最后2,然后再致电。

那么如何找到可变参数的开头?

您总是知道:

  1. 堆栈顶部在哪里。
  2. 在可变参数之前需要(固定)多少个参数。

因此,以相反的顺序推入值是了解变量参数列表从何处开始的最简单方法。您将始终找到固定数量的变量(等于必需(固定)参数的数量,然后是所有变量参数(如果有)),这使计算参数列表的开始成为可能变量的数量,而不需要传递其他信息,换句话说,可变参数的起始位置与堆栈顶部的偏移量始终相同,因为它仅取决于所需参数的数量。


一个例子将使这一点更加清楚。假设一个函数定义为:

1

然后,编译调用int f(int n,...) { // ... } 。在cdecl下,将产生:

f(2,123,456)

push 456 push 123 push 2 call f 启动时,它将找到处于以下状态的堆栈:

f

现在,--- lower addresses ---- [ return address ] <-- esp [ 2 ] [ 123 ] [ 456 ] --- higher addresses --- 很容易知道参数列表从何处开始,知道f是最后一个“固定”(非可变)参数:它只需要计算{{1 }}。也就是说:从n中减去保存的返回地址的固定数量(4),然后为每个固定参数减去4(nb:这是假设esp - 4 - 4)。这样,您将获得第一个可变参数的位置。

这适用于任意数量的可变参数:

esp

现在想象一下相反的情况,即以相反的顺序推入参数,您最终将sizeof(int) == 4编译为:

; f(5,1,3,4,5)      --- lower addresses ----
push 5                     [ return address ] <-- esp
push 4                     [ 5              ]
push 3                     [ 1              ]
push 2                     [ 2              ]
push 1                     [ 3              ]
push 5                     [ 4              ]
call f                     [ 5              ]
                           --- higher addresses ---

然后f(2,456)编译为:

; f(2,456)     --- lower addresses ----
push 2               [ return address   ] <-- esp
push 123             [ 456              ]
push 456             [ 123              ]
call f               [ 2                ]
                     --- higher addresses ---

现在参数列表从哪里开始?无法仅根据堆栈指针(ESP)的值和所需参数的数量来区分,因为距堆栈顶部的偏移量不再相同,但随着数量的变化而变化各种各样的论点。为了弄清楚,您要么必须对基本指针(EBP,假设您的函数甚至不需要使用它就使用它)做一些数学运算,要么传递一些附加信息。


将变量参数推入堆栈时,函数何时知道它们何时结束?

这不是调用约定所建立的。程序员将必须找出一种方法来理解基于非变量参数(或其他参数)的可变参数的数量。例如,在我上面的示例中,我只是将f(5,5)作为第一个参数传递,; f(5,5) --- lower addresses ---- push 5 [ return address ] <-- esp push 1 [ 5 ] push 2 [ 4 ] push 3 [ 3 ] push 4 [ 2 ] push 5 [ 1 ] call f [ 5 ] --- higher addresses --- 函数家族从字符串中格式标识符的数量(例如n,{{ 1}}),printf函数根据系统调用号(第一个参数)将其计算出来,依此类推...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res