【发布时间】:2019-09-03 07:13:48
【问题描述】:
1) C 结构如何在汇编中传递给函数。我的意思是按值传递,而不是按引用传递。 2)顺便说一句,被调用者如何将结构返回给调用者? 由于我的母语不是英语,所以我很抱歉表达不佳。
我编写了一个简单的程序来证明 C 结构是如何传递给函数的。但结果非常令人惊讶。一些值是通过寄存器传递的,但一些值是通过将它们推入堆栈来传递的。这是代码。
源代码
#include <stdio.h>
typedef struct {
int age;
enum {Man, Woman} gen;
double height;
int class;
char *name;
} student;
void print_student_info(student s) {
printf("age: %d, gen: %s, height: %f, name: %s\n",
s.age,
s.gen == Man? "Man":"Woman",
s.height, s.name);
}
int main() {
student s;
s.age = 10;
s.gen = Man;
s.height = 1.30;
s.class = 3;
s.name = "Tom";
print_student_info(s);
return 0;
}
asm
6fa: 55 push %rbp
6fb: 48 89 e5 mov %rsp,%rbp
6fe: 48 83 ec 20 sub $0x20,%rsp
702: c7 45 e0 0a 00 00 00 movl $0xa,-0x20(%rbp)
709: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%rbp)
710: f2 0f 10 05 00 01 00 movsd 0x100(%rip),%xmm0 # 818 <_IO_stdin_used+0x48>
717: 00
718: f2 0f 11 45 e8 movsd %xmm0,-0x18(%rbp)
71d: c7 45 f0 03 00 00 00 movl $0x3,-0x10(%rbp)
724: 48 8d 05 e5 00 00 00 lea 0xe5(%rip),%rax # 810 <_IO_stdin_used+0x40>
72b: 48 89 45 f8 mov %rax,-0x8(%rbp)
72f: ff 75 f8 pushq -0x8(%rbp)
732: ff 75 f0 pushq -0x10(%rbp)
735: ff 75 e8 pushq -0x18(%rbp)
738: ff 75 e0 pushq -0x20(%rbp)
73b: e8 70 ff ff ff callq 6b0 <print_student_info>
740: 48 83 c4 20 add $0x20,%rsp
744: b8 00 00 00 00 mov $0x0,%eax
749: c9 leaveq
74a: c3 retq
74b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
我预计结构是使用堆栈传递给函数的,但上面的代码显示它不是。
【问题讨论】:
-
一般来说,一旦编译的结构就不再存在了。存在的是一个内存区域,并为结构的成员偏移到该内存中。如果没有打包(省略成员之间的填充),您可以确定的唯一偏移量是第一个成员,它将始终具有偏移量
0。结构如何传递给函数是编译器之间可能不同的实现细节。 -
如果我很笨,请原谅我(自从我从事汇编工作很久以来)-但是如果不使用堆栈,那 4 个
pushq指令在做什么? -
@Adrian 我也想知道同样的事情。我见过在 ARM 的寄存器中传递的结构,但这些结构通常要小得多,最多 8 个字节。在这里,在我看来,结构确实是使用堆栈传递的,但我可能弄错了!
-
调用约定由 ABI 为特定系统指定,它应该提到结构。如果不考虑特定的 CPU 和编译器,讨论的意义不大。此外,首先,按值传递结构被认为是非常糟糕的做法。
-
@MichaelPetch 是的。根据程序集标签的标签用法,我们应该始终为目标 ISA 添加一个标签。
标签: c gcc assembly x86-64 calling-convention