汇编中函数返回结构体的方法

代码生成,函数的返回值是个问题,如果返回值是简单类型,如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

恩,这些基本上算是没啥问题了~~:)

你可能感兴趣的:(结构体)