为什么函数局部变量和参数被压入堆栈?

Why functions locals and arguments are pushed to the stack?

函数如何存储在内存中,即:作为普通变量表达式的数据段或代码段 在任何一个中将什么推入堆栈 指针?或者整个函数,如果局部变量和参数已经存储在数据段中,为什么我们还需要推送它们?

例如,在C/C++中,无论何时执行一个函数,执行范围都在堆栈内存中,即在堆栈中为该函数分配内存。这意味着其中的局部变量(包括参数)仅在该堆栈内可用,除非您将参数作为 address/reference 传递而不是按值传递。 在通过 reference/address 传递的情况下,我们使用指针来访问此函数外部存在的变量。

希望它能回答您的问题。如果不是,请提供更多详细信息。

函数存储在内存中。通常使用单独的段来存储可执行代码。内存管理器可以保护此内存不被更改。使用虚拟内存,任何未使用的代码都可以换出,并在代码(函数)必须执行时恢复。

全局数据存储在全局内存中。

本地数据分配在栈上。这些数据称为 "automatic variables".

调用函数时,return地址存储在栈中,前面是函数的参数。

所以,不,函数本身(可执行代码)没有存储在堆栈中。只有它的参数、本地数据和 return 地址。

注意:此简化描述基于英特尔。 "stack".

其他架构可能有不同的概念

虽然细节因平台而异 executable file formats and calling conventions, it's common for programs to be divided into multiple segments. Here's the general layout of a program for x86 (taken from here):

              +------------------------+
high address  | Command line arguments |   
              | and environment vars   |  
              +------------------------+
              |         stack          |
              | - - - - - - - - - - -  |
              |           |            |
              |           V            |
              |                        |
              |           ^            |
              |           |            |
              | - - - - - - - - - - -  |
              |          heap          |
              +------------------------+
              |    global and read-    |
              |       only data        |
              +------------------------+
              |     program text       |
 low address  |    (machine code)      |
              +------------------------+   

函数本身存放在程序正文段中,而函数操作的数据存放在其他段中。请注意,这是 虚拟 内存布局,而不是物理内存。

在大多数系统上,当您调用函数时,会从堆栈中分配一个堆栈帧。堆栈帧将有 space 用于函数参数和局部变量,以及前一个堆栈帧的地址和函数后要执行的下一条指令的地址 returns (同样,具体细节将根据调用约定不同):

              +----------------+
high address: | argument N     |
              +----------------+
              | argument N-1   |
              +----------------+
                     ...
              +----------------+
              | argument 1     |
              +----------------+
              | return addr    |
              +----------------+
              | prv frame addr | <---- %ebp
              +----------------+
              | local 1        |
              +----------------+
              | local 2        |
              +----------------+
                     ...
              +----------------+
 low address: | local N        | <---- %esp
              +----------------+

除了堆栈指针寄存器(x86 上的%esp,x86_64 上的%rsp),还有一个基指针 寄存器( %ebp 在 x86 上,%rsp 在 x86_64 上)。该寄存器存储堆栈帧的地址,函数通过该地址的偏移量引用局部变量和参数。 Quick-n-dirty 示例:

int main( void )
{
  int x = 1;
  int y = 2;

  printf( "foo(1,2) = %d\n", foo( x, y ) );
  return 0;
}

这是编译代码的片段,我们在其中分配 xy(在可执行文件上使用 objdump -d 获得的清单):

 55d:   c7 45 f0 01 00 00 00    movl   [=13=]x1,-0x10(%ebp)
 564:   c7 45 f4 02 00 00 00    movl   [=13=]x2,-0xc(%ebp)

在此代码中,我们将值 1 写入位置 16 字节 "below" 存储在 %ebp 中的地址,并将值 2 写入位置 12 字节 "below" .