搜索

首页  >  问答  >  正文

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

#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: 0x6efea0
Address of b: 0x6efea4
Address of c: 0x6efea8
Address of d: 0x6efeac
Address 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    
PHP中文网PHP中文网2809 天前800

全部回复(2)我来回复

  • 阿神

    阿神2017-04-17 15:41:09

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

    Address of a: 0x7fff3e06b44c
    Address of b: 0x7fff3e06b448
    Address of c: 0x7fff3e06b444
    Address of d: 0x7fff3e06b440
    Address of e: 0x7fff3e06b45c
    Address of f: 0x7fff3e06b458

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

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

    .globl main
        .type    main, @function
    main:
    .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    , %ecx
        movl    , %edx
        movl    , %esi
        movl    , %edi
        call    _Z3fooiiii
        movl    
    .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    , %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
    
    , %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc
    rrreee

    回复
    0
  • 迷茫

    迷茫2017-04-17 15:41:09

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

    回复
    0
  • 取消回复