为什么Windows使用堆栈来存储局部变量?

时间:2013-07-07 17:01:50

标签: c stack internals

为什么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如何知道局部变量的地址(int l_iSumLocalVar,l_iLocalVar1和l_iLocalVar2)?这些变量不是它们存储地址的指针,它们必须从中获取值。我的问题是上面的堆栈。

问题2)如果此函数进一步调用任何其他函数,堆栈将如何表现?据我所知,堆栈将进一步分为自身。如果这是真的,那么被调用函数的局部变量如何从被调用函数中隐藏。基本上局部变量如何维护范围规则?

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

4 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

  

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

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

答案 3 :(得分:0)

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