浅谈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语言函数调用参数压栈的相关问题的全部内容了,希望对大家有所帮助,多多支持毛票票~