찾다

 >  Q&A  >  본문

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일 전805

모든 응답(2)나는 대답할 것이다

  • 阿神

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

    LZ, 어셈블리 코드 일부를 넣고 컴파일할 때 -S만 추가하는 것이 좋습니다.
    귀하의 코드를 실행했는데 결과는 다음과 같습니다.

    으아아아

    e와 a의 차이는 0x10으로 십진수로는 16입니다.

    귀하의 코드를 사용하여 어셈블리 조각을 생성했는데 foo()의 매개변수가 스택에 푸시되지 않은 대신 매개변수가 먼저 레지스터에 제공된 다음 레지스터 값이 스택에 저장되었음을 발견했습니다. , 따라서 귀하의 상황과 유사합니다. 다르게는 a의 주소가 d의 주소보다 높고 이러한 매개변수의 주소가 지역 변수보다 작은 것을 알 수 있습니다.

    으아아아 으아아아

    회신하다
    0
  • 迷茫

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

    이 문제는 플랫폼, 호출 규칙, 컴파일러와 관련이 있습니다.
    i386 플랫폼은 기본 _cdecl 호출 규칙에 따라 런타임 스택에서 함수 매개변수와 함수 로컬 변수 사이에 1: 반환 주소(호출 명령 후 PC), 2: 호출자의 스택 프레임 기본 주소(ebp), 3: 호출 수신자 저장 레지스터(플랫폼에 따라 숫자가 다름), 4: 예외를 처리하기 위해 스택에 정보가 추가됨(컴파일러마다 다른 것을 구현할 수 있으며 gcc에서는 항목을 추가함)

    회신하다
    0
  • 취소회신하다