【问题标题】:What are Call Instructions什么是调用说明
【发布时间】:2018-08-24 03:02:40
【问题描述】:

我目前正在阅读《Programming: Principles and Practice using C++》,作者提到在类定义中编写成员函数的定义可以使函数内联。我不完全确定这意味着什么,所以我查看了https://www.geeksforgeeks.org/inline-functions-cpp/ 以获得更具体的理解。在这句话的上下文中,我似乎无法理解“指令”的含义:

当程序执行函数调用指令时,CPU会存储函数调用之后指令的内存地址。

我用谷歌搜索,看起来调用指令只是将控制权传递给程序的另一部分或另一个应用程序。如果是这个意思,是不是应该说“CPU存储函数调用的调用指令的内存地址”?

这个问题可能听起来很奇怪或挑剔,但我是 CS 新手,真的很想深入了解 CS。

【问题讨论】:

  • 小心:“内联”的 C++ 定义(当函数定义在类定义中时确实会发生)和“内联函数”的编译器优化(该文章讨论)几乎无关的事情。这篇文章在某种程度上谈到了差异,但在我看来并没有很好地解释它。
  • 除了下面的“x86 调用指令”之外,另一个可以跟进的很好的搜索词是关键词“程序计数器”和“调用堆栈”。我建议的搜索短语普遍适用于所有类型的程序集,而不仅仅是 x86。
  • 他们在谈论 x86 call 指令。见Intel's instruction-set reference manual entry for it
  • @aschepler 哦,我不知道内联有两种不同的定义,这可以解释为什么它有点难以理解。我目前只想知道“内联”的 C++ 定义到底是什么。我会尝试搜索。
  • @Peter Cordes,我会查看该链接。由于我还是 CS 新手,所以我部分地想破译 x86 调用指令超出我目前的水平。但我发现我太好奇了,不能忽视并继续前进。

标签: c++ assembly


【解决方案1】:

它们实际上意味着存储函数调用之后的指令的内存地址。这是因为指令在机器代码级别的工作方式。函数调用完成后,程序需要一种方法回到原来的位置。它通过跳转到存储的内存地址的指令来执行此操作,这会导致执行跳转到该指令。如果内存地址指向函数调用,它将永远循环。

【讨论】:

  • 在正常的 asm 术语中,我们会说 call foo返回地址 压入堆栈。 ret 将其弹出(进入程序计数器/指令指针)。所以是的,call 需要推送应该恢复执行的返回地址,而不是它自己的地址。该地址是call 指令最后一个字节之后的字节,即下一条指令的开始。
  • 我比较了解call和return背后的逻辑。也就是说,正如 SoronellHaetir 指出的那样,我知道 CALL 指令允许程序跳转到 call 指令操作数内的地址。阅读每个人的回复,我意识到这个 Call 指令与机器代码(汇编语言)有关。但是,我似乎仍然不明白指令到底是什么。它们是否等同于标准 C++ 编程中的语句/表达式?
  • 处理器只能理解非常基本的操作,例如将整数移动到另一个内存位置或将两个整数相加。指令基本上是发送到处理器的命令,告诉它执行这些基本操作。它们基本上指示处理器在最基本的级别上做什么。语句/表达式可以编译为少至一条指令,也可以编译为实现该语句/表达式所需的多条指令,具体取决于它的复杂程度。
【解决方案2】:

首先,您链接到的页面是在讨论特定类别系统上的行为。它不是在描述标准 C++。

该页面正在讨论可以由 C++ 编译器生成的汇编语言中发生的事情。 “函数调用指令”是指执行函数调用的汇编语言(或机器代码)指令。在 x86 语法中,该指令是 callExample.

您可以通过搜索“x86 调用指令”或类似术语找到更多信息。

被存储的地址,通常称为返回地址,是call之后的下一条指令的地址。当函数执行到ret汇编指令时,跳转到返回地址。

【讨论】:

  • 换句话说,我是否可以将指令视为通过使用函数的地址作为指令的操作数来调用/转到我们想要的任何函数的语句?
  • @davepleteau 这就是call 汇编指令的作用,是的
【解决方案3】:

不,它的意思是(这对于 x86 芯片来说是特定的,其他可能会有所不同)是 CPU 存储调用之后指令的地址(在堆栈顶部),然后跳转到该地址那是调用指令的操作数。当被调用函数执行'ret'指令时,存储的地址被读取并执行跳转到该点。

【讨论】:

  • ...假设函数运行时堆栈指针指向返回地址ret。没有什么魔法可以让这一切发生。 ret 基本上是 pop rip 的一个花哨的名字,初学者经常会犯这个错误,所以值得指出。
【解决方案4】:

当执行 x86 CALL 指令时,程序计数器的内容,即 跟随 CALL 指令的地址,存储在堆栈中,程序控制转移到子程序。

(x86的程序计数器寄存器(IP / EIP / RIP)isn't normally directly accessible,但它被定义为在当前指令执行时指向下一条指令。)

在完成子程序的执行后,将执行 RET 指令,它将堆栈内容(即 CALL 指令之后的指令的地址)加载到程序计数器中。

因此在调用者中在call之后的指令处恢复执行

【讨论】:

  • 欢迎来到 Stack Overflow :) 我对你的回答做了一些小的修改,以更加突出重要的东西,并清理了英文。
猜你喜欢
  • 2014-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-28
  • 2010-11-03
  • 2013-06-22
  • 2012-01-05
相关资源
最近更新 更多