c++ - 函数调用时入栈参数与局部变量在栈中地址问题

浏览:33日期:2023-03-22

问题描述

#include <iostream>int foo(int a, int b, int c, int d) { int e; int f; std::cout << std::hex; std::cout << 'Address of a: ' << &a << std::endl; std::cout << 'Address of b: ' << &b << std::endl; std::cout << 'Address of c: ' << &c << std::endl; std::cout << 'Address of d: ' << &d << std::endl; std::cout << 'Address of e: ' << &e << std::endl; std::cout << 'Address of f: ' << &f << std::endl; return a + b + c + d;} int main() { foo(4, 2, 3, 4); return 0;}

输出:gcc 4.9.2 -32bit release:Address of a: 0x6efea0Address of b: 0x6efea4Address of c: 0x6efea8Address of d: 0x6efeacAddress of e: 0x6efe8c而就我目前的知识了解,栈是从高地址到底地址存储数据,而读取参数的顺序为从右到左,以第一次函数调用为例,入栈顺序应该是:d-c-b-a之后是按顺序将局部变量入栈,即e-f,但是比较e和a的地址可以发现二者在32位下相差14个字节,在64位下相差36个字节,感到比较奇怪,按之前看到的文章:http://blog.csdn.net/tdgx2004...,在局部变量与参数之间应该是函数地址与保护栈底(32位下8个字节),但是实际情况是14个字节,非常好奇还有6个字节装的是什么?求教大神们。下面是foo函数的汇编:foo(int,int,int,int)

0x00401500 <+0>: push %ebp 0x00401501 <+1>: mov %esp,%ebp 0x00401503 <+3>: sub $0x28,%esp 0x00401506 <+6>: movl $0x479590,(%esp) 0x0040150d <+13>: mov $0x488140,%ecx 0x00401512 <+18>: call 0x451580 <_ZNSolsEPFRSt8ios_baseS0_E> 0x00401517 <+23>: sub $0x4,%esp 0x0040151a <+26>: movl $0x489000,0x4(%esp) 0x00401522 <+34>: movl $0x488140,(%esp) 0x00401529 <+41>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x0040152e <+46>: lea 0x8(%ebp),%ecx 0x00401531 <+49>: mov %ecx,(%esp) 0x00401534 <+52>: mov %eax,%ecx 0x00401536 <+54>: call 0x4515c0 <_ZNSolsEPKv> 0x0040153b <+59>: sub $0x4,%esp 0x0040153e <+62>: movl $0x4795c0,(%esp) 0x00401545 <+69>: mov %eax,%ecx 0x00401547 <+71>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x0040154c <+76>: sub $0x4,%esp 0x0040154f <+79>: movl $0x48900f,0x4(%esp) 0x00401557 <+87>: movl $0x488140,(%esp) 0x0040155e <+94>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x00401563 <+99>: mov %eax,%edx 0x00401565 <+101>: lea 0xc(%ebp),%eax 0x00401568 <+104>: mov %eax,(%esp) 0x0040156b <+107>: mov %edx,%ecx 0x0040156d <+109>: call 0x4515c0 <_ZNSolsEPKv> 0x00401572 <+114>: sub $0x4,%esp 0x00401575 <+117>: movl $0x4795c0,(%esp) 0x0040157c <+124>: mov %eax,%ecx 0x0040157e <+126>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x00401583 <+131>: sub $0x4,%esp 0x00401586 <+134>: movl $0x48901e,0x4(%esp) 0x0040158e <+142>: movl $0x488140,(%esp) 0x00401595 <+149>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x0040159a <+154>: mov %eax,%edx 0x0040159c <+156>: lea 0x10(%ebp),%eax 0x0040159f <+159>: mov %eax,(%esp) 0x004015a2 <+162>: mov %edx,%ecx 0x004015a4 <+164>: call 0x4515c0 <_ZNSolsEPKv> 0x004015a9 <+169>: sub $0x4,%esp 0x004015ac <+172>: movl $0x4795c0,(%esp) 0x004015b3 <+179>: mov %eax,%ecx 0x004015b5 <+181>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x004015ba <+186>: sub $0x4,%esp 0x004015bd <+189>: movl $0x48902d,0x4(%esp) 0x004015c5 <+197>: movl $0x488140,(%esp) 0x004015cc <+204>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x004015d1 <+209>: mov %eax,%edx 0x004015d3 <+211>: lea 0x14(%ebp),%eax 0x004015d6 <+214>: mov %eax,(%esp) 0x004015d9 <+217>: mov %edx,%ecx 0x004015db <+219>: call 0x4515c0 <_ZNSolsEPKv> 0x004015e0 <+224>: sub $0x4,%esp 0x004015e3 <+227>: movl $0x4795c0,(%esp) 0x004015ea <+234>: mov %eax,%ecx 0x004015ec <+236>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x004015f1 <+241>: sub $0x4,%esp 0x004015f4 <+244>: movl $0x48903c,0x4(%esp) 0x004015fc <+252>: movl $0x488140,(%esp) 0x00401603 <+259>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x00401608 <+264>: mov %eax,%edx 0x0040160a <+266>: lea -0xc(%ebp),%eax 0x0040160d <+269>: mov %eax,(%esp) 0x00401610 <+272>: mov %edx,%ecx 0x00401612 <+274>: call 0x4515c0 <_ZNSolsEPKv> 0x00401617 <+279>: sub $0x4,%esp 0x0040161a <+282>: movl $0x4795c0,(%esp) 0x00401621 <+289>: mov %eax,%ecx 0x00401623 <+291>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x00401628 <+296>: sub $0x4,%esp 0x0040162b <+299>: movl $0x48904b,0x4(%esp) 0x00401633 <+307>: movl $0x488140,(%esp) 0x0040163a <+314>: call 0x47b500 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc> 0x0040163f <+319>: mov %eax,%edx 0x00401641 <+321>: lea -0x10(%ebp),%eax 0x00401644 <+324>: mov %eax,(%esp) 0x00401647 <+327>: mov %edx,%ecx 0x00401649 <+329>: call 0x4515c0 <_ZNSolsEPKv> 0x0040164e <+334>: sub $0x4,%esp 0x00401651 <+337>: movl $0x4795c0,(%esp) 0x00401658 <+344>: mov %eax,%ecx 0x0040165a <+346>: call 0x451570 <_ZNSolsEPFRSoS_E> 0x0040165f <+351>: sub $0x4,%esp 0x00401662 <+354>: mov 0x8(%ebp),%edx 0x00401665 <+357>: mov 0xc(%ebp),%eax 0x00401668 <+360>: add %eax,%edx 0x0040166a <+362>: mov 0x10(%ebp),%eax 0x0040166d <+365>: add %eax,%edx 0x0040166f <+367>: mov 0x14(%ebp),%eax 0x00401672 <+370>: add %edx,%eax=> 0x00401674 <+372>: leave 0x00401675 <+373>: ret

问题解答

回答1:

LZ,你最好放一段你的汇编代码,编译时候加入 -S 就行了。我运行了下你的代码,我的输出如下:

Address of a: 0x7fff3e06b44cAddress of b: 0x7fff3e06b448Address of c: 0x7fff3e06b444Address of d: 0x7fff3e06b440Address of e: 0x7fff3e06b45cAddress of f: 0x7fff3e06b458

e和a相差0x10也就是10进制的16.

我用你的代码生成了一段汇编,发现这个foo()的参数都没有压入栈,而是将参数先给了register,再将register的值存到stack中,所以和你的情况不一样,你可以看到,a的地址比d还高,并且这些参数的地址都是小于本地变量的。

.globl main .type main, @functionmain:.LFB958: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $4, %ecx movl $3, %edx movl $2, %esi movl $4, %edi call _Z3fooiiii movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc

.LC0: .string 'Address of a: '.LC1: .string 'Address of b: '.LC2: .string 'Address of c: '.LC3: .string 'Address of d: '.LC4: .string 'Address of e: '.LC5: .string 'Address of f: ' .text.globl _Z3fooiiii .type _Z3fooiiii, @function_Z3fooiiii:.LFB957: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $32, %rsp movl %edi, -20(%rbp) movl %esi, -24(%rbp) movl %edx, -28(%rbp) movl %ecx, -32(%rbp) movl $_ZSt3hexRSt8ios_base, %esi movl $_ZSt4cout, %edi call _ZNSolsEPFRSt8ios_baseS0_E movl $.LC0, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -20(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $.LC1, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -24(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $.LC2, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -28(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $.LC3, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -32(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $.LC4, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -4(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl $.LC5, %esi movl $_ZSt4cout, %edi call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc leaq -8(%rbp), %rdx movq %rdx, %rsi movq %rax, %rdi call _ZNSolsEPKv movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi movq %rax, %rdi call _ZNSolsEPFRSoS_E movl -20(%rbp), %edx movl -24(%rbp), %eax addl %eax, %edx movl -28(%rbp), %eax addl %eax, %edx movl -32(%rbp), %eax leal (%rdx,%rax), %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc回答2:

这个问题跟平台和采用的调用惯例以及编译器都有关系。i386平台,默认的_cdecl调用惯例条件下,在运行时栈上,函数参数和函数局部变量中间还有1: 返回地址(pc after call instruction), 2: caller的栈帧基址(ebp), 3: callee save registers(不同平台个数不同),4:为了处理异常而在栈上增加的信息(不同编译器可能实现不同,gcc就会增加东西)

相关文章: