本文作于2015年,备份在这里

只要编程就会涉及函数调用,只要调用函数就会涉及参数与返回值的传递,那么这种传递是如何实现的呢?答案是栈与寄存器。

以下是一个C语言示例:

/*返回参数x乘以2的值*/
#include<stdio.h>
int f(int x)
{
    return x*2;
}
int main(void)
{
    int a;
    scanf("%d",&a);
    a = f(a);
    printf("%d",a);
    return 0;
}

分析:

main()调用f(int)的大致步骤:
1.将形参a压入栈;
2.处理器跳转到f()函数的第一行指令;
3.将实参x出栈;
4.进行运算,得到函数返回值;
5.将返回值放入寄存器EAX;
6.跳转回原函数调用处,如果需要使用返回值,则从寄存器EAX中获取。

既然弄清了传递的机制,那么我们就可以使用内联汇编来模拟这种传递, 以下为对应的程序示例:

#include<stdio.h>
int f(int x)
{
    _asm
    {
        push ebp;备份当前栈底,将EBP压入栈
        mov ebp,esp;将栈顶传送给EBP
        mov EAX,dword ptr [EBP+4];将实参x传送给EAX,至于为什么不直接传送栈顶而是需要后移4个字节,是因为刚刚压入了32位的EBP
        add EAX,EAX;实现乘以2的效果
        pop ebp;出栈,还原EBP
    }
}
int main(void)
{
    int a;
    scanf("%d",&a);
    a = f(a);
    printf("%d",a);
    return 0;
}

经验证,成功与第一个程序实现了同样的功能。

2015.1.22
之前对返回值的分析仅限于x86-32架构的整型数据传递,在x86-64架构中,返回值使用RAX64位寄存器。在两个架构中,返回浮点型都使用FPU单元的浮点寄存器,返回结构体实际上是采用传递指针的做法。
而在ARM的架构中,同样是c语言,处理方式却是不一样的,还不太清楚。
也就是说,c语言只是一种高级语言规范,而他的汇编实现,并不是唯一的,这个是由编译器决定的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注