2011-05-19 94 views
2

我只想更好地理解一个堆栈在地址空间(即你有代码/文本,堆,数据和堆栈)什么是DRAM堆栈(递归期间发生了什么)?

基本上我的理解是堆栈包含局部变量,但是那么数据包含的内容和堆栈包含的内容有什么区别呢?不是数据变量吗?

如果程序对函数a()进行递归调用,是否意味着对于每一个递归级别都有一个新的栈?

+1

堆栈并非真正专用于DRAM。他们是一个数据结构。它由处理器实现以缓解函数调用和递归。我在下面发布了一些细节。 – 2011-05-19 03:14:30

回答

5

堆栈通常与数据的使用和管理方式不同。尽管非局部变量本身通常具有已知的特定内存位置,但堆栈中的内容是相对于寄存器(堆栈指针或基址指针等)找到的。

堆栈通常包含局部变量,传递的参数和用于管理堆栈本身的控制信息。

而且,如果你进行递归调用,你不会得到一个新的堆栈,只是一个新的堆栈帧。框架是与当前堆栈深度相关的堆栈块(无论是通过递归还是只是常规函数调用)。这就是让递归成为可能的原因,即给定深度的变量与其他深度的变量无关。

请记住,这完全取决于架构。我上面的描述是一个常见的情况,但有些架构的堆栈工作方式是不同的,比如SPARC,System z和RCA1802。

更多细节可以发现here(如何框架工作)和here(奇怪的堆栈)。

0

以下程序应该可以帮助您了解正在发生的事情。您将看到文本,bss,堆和堆栈的指针示例。文本通常是可执行代码,bss是静态/全局变量,堆是动态分配的内存,堆栈包含局部变量。

#include <stdlib.h> 
#include <stdio.h> 

#define TESTS 10 
int numfeed = 0; 
int numdead = 0; 

recurse(int x) 
{ 
    u_int pattern=0xfeedface; 
    u_int *otherpattern = malloc(4); 
    *otherpattern = 0xdeadbeef; 

    printf("Feedface %d is at %p\n",x,&pattern); 
    printf("deadbeef %d is at %p\n",x,otherpattern); 

    if (--x == 0) 
    { 
    int *off; 

    for(off = &pattern;numfeed<TESTS;off++) 
    { 
     if (*off == 0xfeedface) 
     printf("Found feedface #%d at %p\n", ++numfeed, off); 
     if (*off == 0xdeadbeef) 
     printf("Found deadbeef #%d at %p -- WHAT?!?!!?!?\n", ++numdead, off); 
    } 
    } 
    else 
    { 
    recurse(x); 
    } 
    // Not freeing otherpattern intentionally. 
} 


main() 
{ 
    u_int *otherpattern = malloc(4); 
    *otherpattern = 0xdeadbeef; 
    int *off; 

    recurse(TESTS); 

    for(off = otherpattern+1;numdead<TESTS;off++) 
    { 
    if (*off == 0xfeedface) 
     printf("Found feedface #%d at %p -- WHAT?!?!!!?!?\n", ++numfeed, off); 
    if (*off == 0xdeadbeef) 
     printf("Found deadbeef #%d at %p\n", 1+TESTS-(++numdead), off); 
    } 

    printf("numfeed is at %p\n",&numfeed); 
    printf("recurse is at %p\n",&recurse); 
} 
1

首先,小澄。堆栈不一定在DRAM中。它们只是一个可以在任何内存中形成的结构:DRAM,缓存,磁盘。

要理解堆栈,您应该先了解什么是堆栈。它像托盘的堆叠,这使它成为一个堆栈的属性是:

  • 你只能访问堆栈的顶部元素
  • 这是后进先出,即,当你去获得来自堆栈的数据可以获取最后存储在堆栈中的数据。

在堆栈中存储某些东西的行为称为PUSH并将其删除称为POP。再说我下面一个空栈:

 
PUSH A 
PUSH B 
PUSH C 

然后堆栈将包含

 
C - Top 
B 
A 

现在如果我执行一个POP(公告不存在操作数在这里),它将返回C和堆栈将包含

 
B -- top of stack 
A 

因此,在处理器堆栈只是上述算法的硬件实现。

甲寄存器包含堆栈称为堆点的顶部的地址 的ISA(指令集架构)提供PUSH和POP指令正如我上面表明访问的堆栈变量。

这是一个非常有用的构造。堆栈用于存储局部变量,基本上是要在函数调用结束时删除的临时数据。它特别有助于函数调用。当一个函数被调用时,新调用函数的局部变量的变量被压入堆栈。

foo(){ 
    int a; 
    int b; // both registers containing a and b are PUSHed here on the stack 
    c = bar(); // pop the stack to get value of c 
    print c 
} 

bar(){ 
    int z; // local variables pushed on top of the stack 
    z = ... 
    return z; // pop all local variables of bar(), then push z on the stack 
} 

我希望上面的帮助。

+0

从我的回忆中,这个答案在许多具体细节上是错误的,虽然在概念上基本上是正确的。特别是,返回变量通常不会(通过)使用堆栈来完成,而是通过根据使用的任何调用约定加载具有返回值的特定寄存器。 – 2011-05-19 03:32:54

+0

有趣的一点。没有理由不使用返回值的使用堆栈。是否使用堆栈实际上是编译器的一个属性,并且与堆栈的语义或者程序的工作无关。实际上,Motorola的HC12编译器使用了该堆栈。使用寄存器是用于保存冗余推送和弹出的优化。如果没有足够的寄存器可用,有时可以使用堆栈。海湾合作委员会有时(很少)这样做,这也解释了为什么6812使用堆栈,因为有很少的寄存器。只是好奇,你有什么其他方式找到我的答案错了? – 2011-05-20 00:53:10

+0

如果'int z'将z压入堆栈,并且'return z'也将z压入堆栈,那么机器代码在返回后将无法重置'SP',而不会破坏返回值;在'RET'指令之前的代码当然不能这样做,但调用代码需要太多关于被调用子例程的知识(除非它将'SP'的原始值存储在寄存器中,我想)。 – 2011-05-20 02:28:21