浅谈C语言函数调用参数压栈的相关问题
参数入栈的顺序
以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左。参数的入栈顺序主要看调用方式,一般来说,__cdecl和__stdcall都是参数从右到左入栈。
看下面的代码:
#include<stdio.h>
inttest(inta,intb)
{
printf("addressofa%x.\n",&a);
printf("addressofb%x.\n",&b);
return0;
}
intmain()
{
test(1,2);
return0;
}
在64位Ubuntu的系统下的运行结果是:
addressofa1ec62c. addressofb1ec628.
32位Ubuntu的结果是:
addressofabfd03290. addressofbbfd03294.
可以看出,首先,不同的体系结构,栈增长的方向也不同,有的是从低地址向高地址方向增长,有的是从高地址向低地址方向增长。
可以用以下的代码来判断栈的增长方向:
typedefenum{
LOW_TO_HIGH,
HIGH_TO_LOW,
LEFT_TO_RIGHT,
RIGHT_TO_LEFT,
}stack_direc_t;
intstack_grow_direc()
{
staticchar*p=NULL;
charc;
if(p==NULL){
p=&c;
stack_grow_direc();
}
else{
printf("Firstinstackaddressis%x.\n",p);
printf("Secondinstackaddressis%x.\n",&c);
if(&c>p){
printf("Stackgrowsfromlowaddresstohighaddress!\n");
returnLOW_TO_HIGH;
}
else{
printf("Stackgrowsfromhighaddresstolowaddress!\n");
returnHIGH_TO_LOW;
}
}
}
函数调用时栈里都有什么
以参数从左到右入栈为例:
pusharg0--HighAddress pusharg1 ... pushargn pusheip pushebp--Lowaddress
32位系统和64位系统函数调用时,参数入栈方式有不同么?
这个问题在不久之前被人问题,当时傻了,我一直以来只关注过32位系统的参数入栈方式,一直以为64位系统也是一样,没有什么不同,现在归纳起来有两点:
64位系统先把传入参数放在寄存器里面,在被调函数的具体实现中把寄存器的值入栈,然后再去栈中取参数
64位系统栈中参数存放的顺序是从左至右的(因为先经历了寄存器传值)
看下面的反汇编:
C代码同上面一样
Ubuntu32位反汇编:
intmain()
{
804846d:55push%ebp
804846e:89e5mov%esp,%ebp
8048470:83e4f0and$0xfffffff0,%esp
8048473:83ec10sub$0x10,%esp
test(1,2);
8048476:c7442404020000movl$0x2,0x4(%esp)
804847d:00
804847e:c7042401000000movl$0x1,(%esp)
8048485:e88affffffcall8048414<test>
return0;
804848a:b800000000mov$0x0,%eax
}
inttest(inta,intb)
{
8048414:55push%ebp
8048415:89e5mov%esp,%ebp
8048417:83ec18sub$0x18,%esp
printf("addressofa%x.\n",&a);
804841a:b860850408mov$0x8048560,%eax
804841f:8d5508lea0x8(%ebp),%edx
8048422:89542404mov%edx,0x4(%esp)
8048426:890424mov%eax,(%esp)
8048429:e812ffffffcall8048340<printf@plt>
return0;
8048466:b800000000mov$0x0,%eax
}
Ubuntu64位反汇编:
intmain()
{
40056e:55push%rbp
40056f:4889e5mov%rsp,%rbp
test(1,2);
400572:be02000000mov$0x2,%esi
400577:bf01000000mov$0x1,%edi
40057c:e8acffffffcallq40052d<test>
return0;
400581:b800000000mov$0x0,%eax
}
inttest(inta,intb)
{
40052d:55push%rbp
40052e:4889e5mov%rsp,%rbp
400531:4883ec10sub$0x10,%rsp
400535:897dfcmov%edi,-0x4(%rbp)
400538:8975f8mov%esi,-0x8(%rbp)
printf("addressofa%x.\n",&a);
40053b:488d45fclea-0x4(%rbp),%rax
40053f:4889c6mov%rax,%rsi
400542:bf14064000mov$0x400614,%edi
400547:b800000000mov$0x0,%eax
40054c:e8bffeffffcallq400410<printf@plt>
return0;
400567:b800000000mov$0x0,%eax
}
看32位的ubuntu操作系统,8048476:的确是把参数直接入栈,2先入栈,1后入栈。
8048476:c7442404020000movl$0x2,0x4(%esp) 804847d:00 804847e:c7042401000000movl$0x1,(%esp) 8048485:e88affffffcall8048414<test>
再来看64位的ubuntu操作系统,2和1根本就没有放入到栈中,而是放到了寄存器esi和edi中。
40056f:4889e5mov%rsp,%rbp test(1,2); 400572:be02000000mov$0x2,%esi 400577:bf01000000mov$0x1,%edi 40057c:e8acffffffcallq40052d<test>
再来看64位系统test的实现,先把edi入栈,再把esi入栈,这就是为什么函数看起来像是从左到右入栈的原因了。
40052d:55push%rbp 40052e:4889e5mov%rsp,%rbp 400531:4883ec10sub$0x10,%rsp 400535:897dfcmov%edi,-0x4(%rbp) 400538:8975f8mov%esi,-0x8(%rbp)
以上就是小编为大家带来的浅谈C语言函数调用参数压栈的相关问题的全部内容了,希望对大家有所帮助,多多支持毛票票~