3

为什么 C 使用堆栈来存储局部变量?这只是为了拥有独立的内存空间还是具有一旦超出范围自动清除所有局部变量和对象的功能?

我还有几个相同的问题,

问题1)如何从指令部分引用局部变量。考虑NewThreadFunc的是由 createThread 函数调用的函数。

DWORD WINAPI NewThreadFunc(PVOID p_pParam)
{
int l_iLocalVar1 = 10;
int l_iLocalVar2 = 20;

int l_iSumLocalVar = l_iLocalVar1 + l_iLocalVar2;
}

这个线程的堆栈看起来像这样,

| p_pParam       |
| NewThreadFunc()|
| 10             |
| 20             |
| 30             |
|                |
.
.
.

现在我的问题是,在执行这个函数时,CPU 怎么知道局部变量(l_iSumLocalVarl_iLocalVar1)的地址l_iLocalVar2?这些变量不是它们存储必须从中获取值的地址的指针。我的问题是上面的堆栈。

问题 2)如果此函数进一步调用任何其他函数,堆栈将如何处理它?据我所知,堆栈会进一步分裂。如果这是真的,被调用函数的局部变量如何从被调用函数中隐藏起来。基本上局部变量如何维护范围规则?

我知道这些可能是非常基本的问题,但有些我怎么想不出答案。

4

4 回答 4

6

首先,将堆栈用于局部变量的不是“Windows”。它与“Windows”或任何其他操作系统完全无关。是您的编译器执行此操作。没有人强迫你的编译器为此目的使用系统堆栈,但通常这是实现局部变量的最简单和最有效的方法。

其次,编译器使用堆栈来存储局部变量(系统提供的堆栈或编译器实现的堆栈)仅仅是因为类似堆栈的存储非常精确地匹配语言要求的局部变量语义。局部变量的存储持续时间由它们的声明性区域(块)定义,这些区域(块)严格相互嵌套。这立即意味着局部变量的存储持续时间遵循 LIFO 原则:后进先出。因此,使用堆栈(一种 LIFO 数据结构)来分配具有 LIFO 存储持续时间的对象是首先想到的也是最自然的事情。

局部变量通常通过它们与当前活动堆栈帧开头的偏移量来寻址。编译器在编译时知道每个局部变量的确切偏移量。编译器通过以下方式生成将为当前函数分配堆栈帧的代码:1)在进入函数时记住堆栈指针的当前位置(假设它存储在寄存器中R1)和 2)将当前堆栈指针移动存储函数的所有局部变量所需的量。一旦以这种方式分配堆栈帧,您的局部变量l_iLocalVar1,l_iLocalVar2l_iSumLocalVar将简单地通过地址访问R1 + 6R1 + 10并且R1 + 14(我使用了任意偏移量)。换句话说,局部变量不会被特定的地址值访问,因为这些地址在编译时是未知的。而是通过计算的地址访问局部变量。它们被计算为一些运行时基地址值 + 一些编译时偏移值。

于 2013-07-07T18:26:27.997 回答
3
  1. 通常,系统调用约定会保留一个寄存器用作“堆栈指针”。局部变量访问是相对于该寄存器的值进行的。由于每个函数都必须知道它使用了多少堆栈空间,因此编译器会发出代码以确保堆栈指针根据每个函数的要求正确调整。

  2. 局部变量的范围仅由编译器强制执行,因为它是一种语言结构,与硬件无关。您可以将堆栈变量的地址传递给其他函数,它们将正常工作。

于 2013-07-07T17:07:25.213 回答
1

为什么堆栈用于局部变量?

好吧,堆栈是一种易于使用的结构,可为临时变量保留空间。它的好处是当函数返回时它几乎会被自动删除。另一种方法是从操作系统分配内存,但这会导致严重的内存碎片。堆栈可以很容易地分配和再次释放,所以它是一个自然的选择。

于 2013-07-07T17:13:00.037 回答
0

所有变量地址都相对于在每次函数调用或返回时递增的堆栈指针。分配和清理这些变量使用的内存的快速简便方法。

于 2013-07-07T17:09:07.900 回答