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

来源:本站
导读:目前正在解读《C语言函数调用参数压栈的相关问题》的相关信息,《C语言函数调用参数压栈的相关问题》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《C语言函数调用参数压栈的相关问题》的详细说明。
简介:本文主要为大家详细讲解一下C语言函数调用参数压栈的相关问题。

参数入栈的顺序

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

<code class="hljs cpp">#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编译的栈是从高地址向低地址生长的,也就是说谁先入栈,谁的地址就大,掌握了这一点,应试不难写出代码.

函数调用时栈里都有什么

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

<code class="hljs r">push arg0 -- High Address

push arg1

...

push argn

push eip

push ebp -- Low address

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

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

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

看下面的反汇编:

<code class="hljs perl">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

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

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后入栈。

<code class="hljs http"> 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

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

<code class="hljs perl"> 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

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

<code class="hljs http"> 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语言函数调用参数压栈的相关问题》最后刷新时间 2024-03-14 00:59:12,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《C语言函数调用参数压栈的相关问题》该内容的真实性请自行鉴别。