我得到了下面的C代码:
#include <stdio.h>
int function(int a, int b)
{
int res = a + b;
return res;
}
int main(){
function(1,2);
exit(0);
}
我用gcc 4.8.2(在Ubuntu 14下)为x86-64编译它,它产生了这样的代码:
000000000040052d <function>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: 89 7d ec mov %edi,-0x14(%rbp)
400534: 89 75 e8 mov %esi,-0x18(%rbp)
400537: 8b 45 e8 mov -0x18(%rbp),%eax
40053a: 8b 55 ec mov -0x14(%rbp),%edx
40053d: 01 d0 add %edx,%eax
40053f: 89 45 fc mov %eax,-0x4(%rbp)
400542: 8b 45 fc mov -0x4(%rbp),%eax
400545: 5d pop %rbp
400546: c3 retq
有些事情我看不懂。
一开始,我们推送了 rbp,并将 rsp 保存在 rbp 中。然后在堆栈的顶部(在%rbp处),我们保存了rbp。那么低于 rbp 的所有内容都是可用空间。
但是,然后我们将来自 edi 和 esi 的传递参数放在 -0x14(%rbp) 及以下位置。
但是为什么我们不能把它们放在rbp/rsp所指向的正下方呢?edi和esi都是4字节长,那为什么不是-0x8(%rbp)和-0xc(%rbp)呢?和内存对齐有联系吗?
为什么会有一个奇怪的保存eax的堆栈,并在返回之前读取它?
首先,请注意,您正在查看未优化的编译器输出。关闭优化后,编译器输出通常看起来有点愚蠢,因为编译器实际上将C语言的每一行翻译成等效的汇编运行,而无需进行最简单、最明显的优化。
对于你的第一个问题,答案是“因为这是你的编译器决定变量应该去的地方”。没有更好的答案 - 编译器在堆栈分配方案方面差异很大。例如,我的计算机上的 Clang 会输出以下内容:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %esi
addl -8(%rbp), %esi
movl %esi, -12(%rbp)
movl -12(%rbp), %eax
popq %rbp
retq
其中,您可以清楚地看到a
存储在-4,b
存储在-8,
对于你的第二个问题,让我们看看指令是如何映射到C的:
标准函数序言(设置堆栈帧):
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
将两个参数存储到堆栈变量< code>a和< code>b中:
400531: 89 7d ec mov %edi,-0x14(%rbp)
400534: 89 75 e8 mov %esi,-0x18(%rbp)
加载a和
b
400537: 8b 45 e8 mov -0x18(%rbp),%eax
40053a: 8b 55 ec mov -0x14(%rbp),%edx
实际上做
a b
40053d: 01 d0 add %edx,%eax
设置
result=(a b的结果)
40053f: 89 45 fc mov %eax,-0x4(%rbp)
将
结果
复制到返回值(返回结果;
)
400542: 8b 45 fc mov -0x4(%rbp),%eax
实际返回:
400545: 5d pop %rbp
400546: c3 retq
因此,您可以看到
eax
的冗余保存和加载只是因为保存和加载对应于原始C文件的不同语句:保存来自结果=
,加载来自返回结果;
。
相比之下,这里是Clang的优化输出(
-O
):
pushq %rbp
movq %rsp, %rbp
addl %esi, %edi
movl %edi, %eax
popq %rbp
retq
更聪明:没有堆栈操作,整个函数体只是两个指令
addl
和movl
。(当然,如果您声明函数静态
,那么GCC和Clang都会高兴地检测到该函数从未被有效使用,并直接删除它。)
AWS S3 Web控制台列出了已删除的存储桶。选择桶并尝试清空或删除它会导致“清空桶”或“删除桶”模式确认按钮静默失败。 如果您单击桶并尝试上传文件,您会收到一条错误消息,“找不到错误数据”。如果您尝试创建一个文件夹,您会得到,“错误无法创建带名称的文件夹”。"如果尝试更改和属性、权限或管理,也会收到错误消息。 如果您尝试使用相同的名称创建一个bucket(可能是为了覆盖旧的bucket),则会
创建套接字并建立连接后,我使用BufferedReader和PrintWriter从套接字进行写/读操作。 当我用完插座后,我就把它关上。 这将导致发送一个TCP fin,并留下一个半开的传输控制协议。有什么方法可以检测客户端/服务器是否关闭了连接?
我是Spring Boot的新手,我有一个Spring Boot应用程序似乎忽略了@PreDestroy注释-当我从命令行或通过Eclipse运行时,我从来没有看到@PreDestroy代码在应用程序关闭时运行(例如通过ctrl-c) 代码如下。。。 应用java: 消息处理器配置: 消息处理器:
问题内容: 我有一个扩展LinkedList类的类。这是一段代码摘录: 预期会返回自动拆箱的int值。但是由于某种原因,编译器会抛出一个错误,指出类型不兼容,所需的类型为int,找到的类型为Integer。这可以在不同的类中很好地工作!是什么赋予了?:( 问题答案: 这是因为你有追求。 通常,您使用类型参数:,但是使用了。也就是说,您创建了一个类型参数(阴影)。 就目前而言,您的课程相当于 删除t
问题内容: 如何在GDB中永久更改反汇编样式。我尝试过:在GDB中,但是稍后启动GDB时,它仍然具有att风格。 问题答案: gdb启动时会执行〜/ .gdbinit文件(如果存在);您应该能够添加行 对它。
但是我只想执行一次,@在场景大纲之前,@在场景大纲之后!在cucumber-JVM中有什么方法可以实现这一点吗?