代碼生成,函數的返回值是個問題,如果返回值是簡單類型,如int, char等,一個字節可以容納,那編譯器的做法是將值直接存在eax寄存器中.
代碼為證
c代碼:
#include <stdio.h> int add(int a, int b){ return a + b; } int main(){ int a = add(2,3); return 0; }
gcc -S add.c
add.s匯編代碼:
.globl add .type add, @function add: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax movl 8(%ebp), %edx leal (%edx,%eax), %eax popl %ebp ret .size add, .-add .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $24, %esp movl $3, 4(%esp) movl $2, (%esp) call add movl %eax, -4(%ebp) movl $0, %eax leave ret
那么如果返回一個結構體,寄存器無法存下怎么辦呢?
方法是, 調用者在調用的時候,先把需要的參數反向入棧, 最后再把變量的地址入棧,如 struct A t = get_struct(int a);
則先int a 入棧,然后將t的地址放入eax, pushl %eax.
在被調用的函數中,我們就完成了返回值的賦值工作, 因為有了返回值賦值的變量的地址,所以依次將struct的各成員變量賦值給返回變量的對應內存即可.
好吧,上代碼
c代碼:
#include <stdio.h> struct A{ int a; char c; }; struct A add(int a, int b){ struct A t; t.a = a*b; return t; } int main(){ struct A t = add(3, 4); printf("%c\n", t.c); return 0; }
gcc -S temp.c
temp.s:
.globl add .type add, @function add: pushl %ebp movl %esp, %ebp subl $16, %esp movl 8(%ebp), %ecx # 8(%ebp)最后一個參數,就是存儲了返回變量在調用者中的地址,即調用前main中的%eax的值 movl 12(%ebp), %eax #取出第二個int 參數 imull 16(%ebp), %eax #取出第一個int參數,記住,參數反向入棧,后面的參數在低地址 movl %eax, -8(%ebp) #-8(%ebp) ~ (%ebp)存儲的是局部結構體變量t movl -8(%ebp), %eax #依次將局部變量的各個成員變量移動到寄存器 movl -4(%ebp), %edx movl %eax, (%ecx) #賦值給傳入地址的調用者的變量 movl %edx, 4(%ecx) movl %ecx, %eax leave ret $4 .LC0: .string "%c\n" .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $36, %esp #為什么是36呢? main函數的int argc, char *argv[]8個字節, #struct A t 局部變量8個字節,然后add()調用12個字節--其中有一個是隱式的入棧,%eax記錄 #局部變量的地址, 然后printf8個字節 leal -16(%ebp), %eax movl $4, 8(%esp) #參數入棧,這兒入棧沒有用pushl,而是先subl esp,預留好空間,然后movl,是等效的 movl $3, 4(%esp) movl %eax, (%esp) #需要賦值的局部變量的地址入棧,有了這個地址,賦值工作就在add()zh中完成了 call add subl $4, %esp movzbl -12(%ebp), %eax movsbl %al,%edx movl $.LC0, %eax movl %edx, 4(%esp) movl %eax, (%esp) call printf movl $0, %eax movl -4(%ebp), %ecx leave leal -4(%ecx), %esp ret
好了,基本上解決問題了,知道碰到struct A function();的賦值問題怎么解決了, 但還有個問題, 如果沒有賦值,我只是調用add(3, 4);
那調用者的%eax,應該把什么地址入棧呢?
經過實驗法現,不管你有沒i有var = add(3,4); 編譯器都回在調用者的棧上預留一個局部變量的空間,然后把這個變量地址 -> eax -> pushl stack
恩,這些基本上算是沒啥問題了~~:)