C语言函数调用参数压栈的相关问题.docx
《C语言函数调用参数压栈的相关问题.docx》由会员分享,可在线阅读,更多相关《C语言函数调用参数压栈的相关问题.docx(13页珍藏版)》请在冰豆网上搜索。
C语言函数调用参数压栈的相关问题
参数入栈的顺序
以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左。
参数的入栈顺序主要看调用方式,一般来说,__cdecl和__stdcall都是参数从右到左入栈。
看下面的代码:
#include
inttest(inta,intb)
{
printf("addressofa%x.\n",&a);
printf("addressofb%x.\n",&b);
return0;
}
intmain()
{
test(1,2);
return0;
}
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
在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;
}
}
}
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
∙15
∙16
∙17
∙18
∙19
∙20
∙21
∙22
∙23
∙24
∙25
∙26
∙27
∙28
∙29
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
∙15
∙16
∙17
∙18
∙19
∙20
∙21
∙22
∙23
∙24
∙25
∙26
∙27
∙28
∙29
函数调用时栈里都有什么
以参数从左到右入栈为例:
pusharg0--HighAddress
pusharg1
...
pushargn
pusheip
pushebp--Lowaddress
∙1
∙2
∙3
∙4
∙5
∙6
∙1
∙2
∙3
∙4
∙5
∙6
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
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
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
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
return0;
400567:
b800000000mov$0x0,%eax
}
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
∙15
∙16
∙17
∙18
∙19
∙20
∙21
∙22
∙23
∙24
∙25
∙26
∙27
∙28
∙29
∙30
∙31
∙32
∙33
∙34
∙35
∙36
∙37
∙38
∙39
∙40
∙41
∙42
∙43
∙44
∙45
∙46
∙47
∙48
∙49
∙50
∙51
∙52
∙53
∙54
∙55
∙56
∙57
∙58
∙59
∙60
∙1
∙2
∙3
∙4
∙5
∙6
∙7
∙8
∙9
∙10
∙11
∙12
∙13
∙14
∙15
∙16
∙17
∙18
∙19
∙20
∙21
∙22
∙23
∙24
∙25
∙26
∙27
∙28
∙29
∙30
∙31
∙32
∙33
∙34
∙35
∙36
∙37
∙38
∙39
∙40
∙41
∙42
∙43
∙44
∙45
∙46
∙47
∙48
∙49
∙50
∙51
∙52
∙53
∙54
∙55
∙56
∙57
∙58
∙59
∙60
看32位的ubuntu操作系统,8048476:
的确是把参数直接入栈,2先入栈,1后入栈。
8048476:
c7442404020000movl$0x2,0x4(%esp)
804847d:
00
804847e:
c7042401000000movl$0x1,(%esp)
8048485:
e88affffffcall8048414
∙1
∙2
∙3
∙4
∙1
∙2
∙3
∙4
再来看64位的ubuntu操作系统,2和1根本就没有放入到栈中,而是放到了寄存器esi和edi中。
40056f:
4889e5mov%rsp,%rbp
test(1,2);
400572:
be02000000mov$0x2,%esi
400577:
bf01000000mov$0x1,%edi
40057c:
e8acffffffcallq40052d
∙1
∙2
∙3
∙4
∙5
∙1
∙2
∙3
∙4
∙5
再来看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)
∙1
∙2
∙3
∙4
∙5
∙1
∙2
∙3
∙4
∙5