从技术上讲,可变参数函数是如何工作的? printf 是如何工作的?

2023-12-03

我知道我可以使用va_arg编写我自己的可变参数函数,但是可变参数函数在幕后如何工作,即在汇编指令级别?

例如,怎么可能printf需要可变数量的参数?


* No rule without exception. There is no language C/C++, however, this question can be answered for both of them

* Note: Answer originally given to How can printf function can take variable parameters in number while output them?, but it seems it did not apply to the questioner


C 和 C++ 标准对其工作方式没有任何要求。符合要求的编译器很可能决定发出链表,std::stack<boost::any>甚至是引擎盖下的神奇小马灰尘(根据@Xeo的评论)。

然而,它通常按如下方式实现,即使诸如内联或在 CPU 寄存器中传递参数之类的转换可能不会留下任何所讨论的代码。

另请注意,该答案具体描述了下面视觉效果中向下增长的堆栈;另外,这个答案是一个简化,只是为了演示该方案(请参阅https://en.wikipedia.org/wiki/Stack_frame).

如何使用不固定数量的参数调用函数

这是可能的,因为底层机器架构对于每个线程都有一个所谓的“堆栈”。堆栈用于将参数传递给函数。例如,当您有:

foobar("%d%d%d", 3,2,1);

然后编译成这样的汇编代码(示例性和示意性的,实际代码可能看起来不同);请注意,参数是从右向左传递的:

push 1
push 2
push 3
push "%d%d%d"
call foobar

这些压入操作会填满堆栈:

              []   // empty stack
-------------------------------
push 1:       [1]  
-------------------------------
push 2:       [1]
              [2]
-------------------------------
push 3:       [1]
              [2]
              [3]  // there is now 1, 2, 3 in the stack
-------------------------------
push "%d%d%d":[1]
              [2]
              [3]
              ["%d%d%d"]
-------------------------------
call foobar   ...  // foobar uses the same stack!

底部堆栈元素称为“堆栈顶部”,通常缩写为“TOS”。

The foobar函数现在将访问堆栈,从 TOS 开始,即格式字符串,正如您所记得的,它是最后推送的。想象stack是你的堆栈指针,stack[0]是 TOS 处的值,stack[1]是高于 TOS 的一项,依此类推:

format_string <- stack[0]

...然后解析格式字符串。解析时,它识别%d-tokens,对于每一个,从堆栈中加载一个值:

format_string <- stack[0]
offset <- 1
while (parsing):
    token = tokenize_one_more(format_string)
    if (needs_integer (token)):
        value <- stack[offset]
        offset = offset + 1
    ...

这当然是一个非常不完整的伪代码,它演示了函数如何必须依赖传递的参数来找出它必须从堆栈中加载和删除的量。

Security

这种对用户提供的参数的依赖也是目前最大的安全问题之一(请参阅https://cwe.mitre.org/top25/)。用户可能很容易错误地使用可变参数函数,要么因为他们没有阅读文档,要么忘记调整格式字符串或参数列表,要么因为它们是邪恶的,或者其他什么。也可以看看格式化字符串攻击.

C 实施

在 C 和 C++ 中,可变参数函数与va_list界面。虽然压入堆栈是这些语言固有的(在 K+R C 中,你甚至可以前向声明一个函数而不声明它的参数,但仍然使用任何数量和种类的参数来调用它),从这样一个未知的参数列表中读取是通过接口进行的va_...- 宏和va_list-type,它基本上抽象了低级堆栈帧访问。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从技术上讲,可变参数函数是如何工作的? printf 是如何工作的? 的相关文章

  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 秒表有最长运行时间吗?

    多久可以Stopwatch在 NET 中运行 如果达到该限制 它会回绕到负数还是从 0 重新开始 Stopwatch Elapsed返回一个TimeSpan From MSDN https learn microsoft com en us
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 为什么当实例化新的游戏对象时,它没有向它们添加标签? [复制]

    这个问题在这里已经有答案了 using System Collections using System Collections Generic using UnityEngine public class Test MonoBehaviou
  • 从Web API同步调用外部api

    我需要从我的 Web API 2 控制器调用外部 api 类似于此处的要求 使用 HttpClient 从 Web API 操作调用外部 HTTP 服务 https stackoverflow com questions 13222998
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 带动态元素的 WPF 启动屏幕。如何?

    我是 WPF 新手 我需要一些帮助 我有一个加载缓慢的 WPF 应用程序 因此我显示启动屏幕作为权宜之计 但是 我希望能够在每次运行时更改屏幕 并在文本区域中显示不同的引言 这是一个生产力应用程序 所以我将使用非愚蠢但激励性的引言 当然 如
  • 重载<<的返回值

    include
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 向现有 TCP 和 UDP 代码添加 SSL 支持?

    这是我的问题 现在我有一个 Linux 服务器应用程序 使用 C gcc 编写 它与 Windows C 客户端应用程序 Visual Studio 9 Qt 4 5 进行通信 是什么very在不完全破坏现有协议的情况下向双方添加 SSL
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在

随机推荐