2013-07-07 69 views
3

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

我身边一样,

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

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

+5

这与Windows有什么关系? – 2013-07-07 17:05:19

+3

C没有指定(本地)变量的存储位置或方式,因此它不一定是堆栈。 – Kninnug

+0

确实如此,但实际上每个实现都使用堆栈,因为这就是世界上所有硬件的工作原理。你知道有什么例外吗? –

回答

5

首先,它不是使用堆栈作为局部变量的“Windows”。它与“Windows”或其他任何操作系统完全无关。这是你的编译器。没有人强迫你的编译器使用系统堆栈,但通常这是实现局部变量的最简单和最有效的方式。其次,编译器使用堆栈来存储局部变量(是系统提供的堆栈或编译器实现的堆栈),因为堆栈式存储非常精确地匹配局部变量的语言规定语义。局部变量的存储时间由它们的声明区域(块)定义,这些区域严格嵌套在一起。这立即意味着局部变量的存储持续时间遵循LIFO原则:后进先出。因此,使用堆栈(LIFO数据结构)来分配具有LIFO存储时间的对象是首先也是最自然的想法。

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

+0

我喜欢你的答案。有一点仍不明确,那就是变量的范围如何维护? 你说它是在堆栈的帮助下处理的,它在超出范围时弹出。那么,这是否意味着即使是局部范围,curley brace在for循环中,创建一个新的子stach,但堆栈指针不会移动到新的堆栈基址,从而使得以前的局部变量可以与新的变量一起在新的范围内使用大括号... main() {intl_iVar1 = 2; (;;) { int l_iVar2 = 5; } } –

+0

继续.. 在我上面的代码示例中,我想指出两个变量都可见的for循环中的作用域区域。 感谢您的时间@AndreyT –

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

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

+0

对2的最后一句的评论:当将局部变量的地址传递给其他函数时,请小心,因为当变量超出范围时这些地址将变为无效。 – Kninnug

+0

@Kninnug:正确。指向堆栈变量的规则:您可以“传入”,但不能“传出”。也就是说,您可以将它作为参数传递给您调用的内容,但不能将其作为返回值传递出去,因为这些变量将超出范围。 –

+1

@JoeZ,取决于你“传入”的函数是否要保持对指针的引用。我认为你的规则太简单了。 –

0

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

1

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

那么,堆栈是一个易于使用的结构来为临时变量保留空间。它具有这样的好处,即它在函数返回时几乎可以自动移除。另一种方法是从操作系统分配内存,但这会导致内存碎片过多。 堆栈可以很容易地分配以及再次释放,所以这是一个自然的选择。

+0

“另一种方法是从操作系统分配内存” - 嗯?你认为堆栈的内存来自哪里? – 2013-07-07 17:42:59

+0

@ H2CO3我的意思是,你可以使用malloc/free调用内部分配自动变量,但这会带来额外的开销。所有的内存都来自操作系统。没有这个Te栈已经可用。 – Devolus

相关问题