C语言基本类型的返回通过寄存器实现。
寄存器容量不大,一个结构体的大小不定,多塞几个成员,很容易就会超过寄存器的容量,不太可能通过寄存器一次性返回一个结构体。
把以下代码转为汇编,探个究竟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// return_struct.c struct the_struct { int num1; int num2; double num3; double num4; int *num_ptr1; int *num_ptr2; }; struct the_struct fun() { struct the_struct the_struct1; the_struct1.num1 = 1; the_struct1.num2 = 2; return the_struct1; } int get_struct() { struct the_struct the_struct1 = fun(); return the_struct1.num1 + 1; } |
优化后的代码可能比较晦涩,64位寄存器数量是32位的一倍,为了简便理解,关闭代码优化,并且转为32位汇编,命令:
1 |
bash # gcc -O0 -S -masm=intel -m32 return_struct.c |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
.file "return_struct.c" .intel_syntax noprefix .text .globl _fun .def _fun; .scl 2; .type 32; .endef _fun: push ebp mov ebp, esp sub esp, 32 ; 为结构体在栈帧中分配32字节内存 mov DWORD PTR [ebp-32], 1 ; *(ebp - 32) = 1 mov DWORD PTR [ebp-28], 2 ; *(ebp - 28) = 1 ; ebp指向的位置储存着get_struct()函数的ebp值 ; ebp+4指向返回地址 ; ebp+8是get_struct()函数中结构体的指针 mov eax, DWORD PTR [ebp+8] ; eax = get_struct()函数中结构体的指针 mov edx, DWORD PTR [ebp-32] ; edx是fun()函数中结构体首DWORD的值 mov DWORD PTR [eax], edx ; *eax = edx,让get_struct()中的结构体第一个DWORD = edx ; 结构体剩余的其余值传值 mov edx, DWORD PTR [ebp-28] mov DWORD PTR [eax+4], edx mov edx, DWORD PTR [ebp-24] mov DWORD PTR [eax+8], edx mov edx, DWORD PTR [ebp-20] mov DWORD PTR [eax+12], edx mov edx, DWORD PTR [ebp-16] mov DWORD PTR [eax+16], edx mov edx, DWORD PTR [ebp-12] mov DWORD PTR [eax+20], edx mov edx, DWORD PTR [ebp-8] mov DWORD PTR [eax+24], edx mov edx, DWORD PTR [ebp-4] mov DWORD PTR [eax+28], edx mov eax, DWORD PTR [ebp+8] leave ret 4 .globl _get_struct .def _get_struct; .scl 2; .type 32; .endef _get_struct: push ebp mov ebp, esp sub esp, 40 ; 分配40字节内存 lea eax, [ebp-32] ; eax = ebp - 32,eax是结构体的指针 mov DWORD PTR [esp], eax ; *esp储存结构体的指针,即get_struct()函数栈顶是结构体的指针 call _fun sub esp, 4 mov eax, DWORD PTR [ebp-32] ; (ebp - 32)是结构体的地址 add eax, 1 leave ret .ident "GCC: (GNU) 5.3.0" |
汇编代码已写上注释。
在get_struct()函数中,调用fun()之前,会首先把get_struct()函数中要接收返回值的地址压入栈中。
在fun()中,要返回值时,直接从get_struct()的栈帧取得保存结构体值的地址,直接对get_struct()的栈帧进行操作。
总的来说,一个函数要返回一个结构体,则从调用者函数的栈帧中取得接收结构体的地址(被赋值结构体的指针),直接对调用者函数的栈帧进行修改。