码迷,mamicode.com
首页 > 编程语言 > 详细

C语言函数调用参数压栈的相关问题

时间:2015-04-20 20:58:12      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:

参数入栈的顺序

以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左。当时没有想清楚,随口就说从右向左。其实这个回答是不完全正确的。因为其实入栈的顺序,不同的体系架构是不一样的,举例来说, 看下面的代码:

#include <stdio.h>

int test(int a, int b)
{
    printf("address of a %x.\n", &a);
    printf("address of b %x.\n", &b);
    if ((unsigned int)&a > (unsigned int)&b) {
        printf("Push argument from left to right...\n");
    }
    else {
        printf("Push argument from right to left...\n");
    }
    return 0;
}

int main()
{
    test(1, 2);
    return 0;
}

在64位Ubuntu的系统下的运行结果是:从左到右。

address of a 1ec62c.
address of b 1ec628.
Push argument from left to right…

32位Ubuntu的结果是:从右到左

address of a bfd03290.
address of b bfd03294.
Push argument from right to left…

先来解释一下为什么上面的代码能够判别入栈的顺序,首先你要明白一点:

C语言中的gcc编译的栈是从高地址向低地址生长的,也就是说谁先入栈,谁的地址就大,掌握了这一点,应试不难写出代码.

函数调用时栈里都有什么

以参数从左到右入栈为例:

push arg0 -- High Address
push arg1
...
push argn
push eip
push ebp  -- Low address

32位系统和64位系统函数调用时,参数入栈方式有不同么?

这个问题在不久之前被人问题,当时傻了,我一直以来只关注过32位系统的参数入栈方式,一直以为64位系统也是一样,没有什么不同,现在归纳起来有两点:

  • 64位系统先把传入参数放在寄存器里面,在被调函数的具体实现中把寄存器的值入栈,然后再去栈中取参数
  • 64位系统栈中参数存放的顺序是从左至右的(因为先经历了寄存器传值)

看下面的反汇编:

C代码同上面一样
Ubuntu 32位反汇编:
int main()
{
 804846d:   55                      push   %ebp
 804846e:   89 e5                   mov    %esp,%ebp
 8048470:   83 e4 f0                and    $0xfffffff0,%esp
 8048473:   83 ec 10                sub    $0x10,%esp
    test(1, 2);
 8048476:   c7 44 24 04 02 00 00    movl   $0x2,0x4(%esp)
 804847d:   00 
 804847e:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048485:   e8 8a ff ff ff          call   8048414 <test>
    return 0;
 804848a:   b8 00 00 00 00          mov    $0x0,%eax
}
int test(int a, int b)
{
 8048414:   55                      push   %ebp
 8048415:   89 e5                   mov    %esp,%ebp
 8048417:   83 ec 18                sub    $0x18,%esp
    printf("address of a %x.\n", &a);
 804841a:   b8 60 85 04 08          mov    $0x8048560,%eax
 804841f:   8d 55 08                lea    0x8(%ebp),%edx
 8048422:   89 54 24 04             mov    %edx,0x4(%esp)
 8048426:   89 04 24                mov    %eax,(%esp)
 8048429:   e8 12 ff ff ff          call   8048340 <printf@plt>

    return 0;
 8048466:   b8 00 00 00 00          mov    $0x0,%eax
}
Ubuntu 64位反汇编:
int main()
{
  40056e:   55                      push   %rbp
  40056f:   48 89 e5                mov    %rsp,%rbp
    test(1, 2);
  400572:   be 02 00 00 00          mov    $0x2,%esi
  400577:   bf 01 00 00 00          mov    $0x1,%edi
  40057c:   e8 ac ff ff ff          callq  40052d <test>
    return 0;
  400581:   b8 00 00 00 00          mov    $0x0,%eax
}
int test(int a, int b)
{
  40052d:   55                      push   %rbp
  40052e:   48 89 e5                mov    %rsp,%rbp
  400531:   48 83 ec 10             sub    $0x10,%rsp
  400535:   89 7d fc                mov    %edi,-0x4(%rbp)
  400538:   89 75 f8                mov    %esi,-0x8(%rbp)
    printf("address of a %x.\n", &a);
  40053b:   48 8d 45 fc             lea    -0x4(%rbp),%rax
  40053f:   48 89 c6                mov    %rax,%rsi
  400542:   bf 14 06 40 00          mov    $0x400614,%edi
  400547:   b8 00 00 00 00          mov    $0x0,%eax
  40054c:   e8 bf fe ff ff          callq  400410 <printf@plt>

    return 0;
  400567:   b8 00 00 00 00          mov    $0x0,%eax
}

看32位的ubuntu操作系统, 8048476: 的确是把参数直接入栈,2先入栈,1后入栈。

 8048476:   c7 44 24 04 02 00 00    movl   $0x2,0x4(%esp)
 804847d:   00 
 804847e:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048485:   e8 8a ff ff ff          call   8048414 <test>

再来看64位的ubuntu操作系统,2 和1根本就没有放入到栈中,而是放到了寄存器esi和edi中。

  40056f:   48 89 e5                mov    %rsp,%rbp
    test(1, 2);
  400572:   be 02 00 00 00          mov    $0x2,%esi
  400577:   bf 01 00 00 00          mov    $0x1,%edi
  40057c:   e8 ac ff ff ff          callq  40052d <test>

再来看64位系统test的实现,先把edi入栈,再把esi入栈,这就是为什么函数看起来像是从左到右入栈的原因了。

  40052d:   55                      push   %rbp
  40052e:   48 89 e5                mov    %rsp,%rbp
  400531:   48 83 ec 10             sub    $0x10,%rsp
  400535:   89 7d fc                mov    %edi,-0x4(%rbp)
  400538:   89 75 f8                mov    %esi,-0x8(%rbp)

C语言函数调用参数压栈的相关问题

标签:

原文地址:http://blog.csdn.net/huangkangying/article/details/45154983

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!