2011-05-19 72 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