匯編中函數返回結構體的方法


代碼生成,函數的返回值是個問題,如果返回值是簡單類型,如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

恩,這些基本上算是沒啥問題了~~:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM