我只想更好地理解一個堆棧在地址空間(即你有代碼/文本,堆,數據和堆棧)什麼是DRAM堆棧(遞歸期間發生了什麼)?
基本上我的理解是堆棧包含局部變量,但是那麼數據包含的內容和堆棧包含的內容有什麼區別呢?不是數據變量嗎?
如果程序對函數a()進行遞歸調用,是否意味着對於每一個遞歸級別都有一個新的棧?
我只想更好地理解一個堆棧在地址空間(即你有代碼/文本,堆,數據和堆棧)什麼是DRAM堆棧(遞歸期間發生了什麼)?
基本上我的理解是堆棧包含局部變量,但是那麼數據包含的內容和堆棧包含的內容有什麼區別呢?不是數據變量嗎?
如果程序對函數a()進行遞歸調用,是否意味着對於每一個遞歸級別都有一個新的棧?
堆棧通常與數據的使用和管理方式不同。儘管非局部變量本身通常具有已知的特定內存位置,但堆棧中的內容是相對於寄存器(堆棧指針或基址指針等)找到的。
堆棧通常包含局部變量,傳遞的參數和用於管理堆棧本身的控制信息。
而且,如果你進行遞歸調用,你不會得到一個新的堆棧,只是一個新的堆棧幀。框架是與當前堆棧深度相關的堆棧塊(無論是通過遞歸還是隻是常規函數調用)。這就是讓遞歸成爲可能的原因,即給定深度的變量與其他深度的變量無關。
請記住,這完全取決於架構。我上面的描述是一個常見的情況,但有些架構的堆棧工作方式是不同的,比如SPARC,System z和RCA1802。
以下程序應該可以幫助您瞭解正在發生的事情。您將看到文本,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);
}
首先,小澄。堆棧不一定在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
}
我希望上面的幫助。
從我的回憶中,這個答案在許多具體細節上是錯誤的,雖然在概念上基本上是正確的。特別是,返回變量通常不會(通過)使用堆棧來完成,而是通過根據使用的任何調用約定加載具有返回值的特定寄存器。 – 2011-05-19 03:32:54
有趣的一點。沒有理由不使用返回值的使用堆棧。是否使用堆棧實際上是編譯器的一個屬性,並且與堆棧的語義或者程序的工作無關。實際上,Motorola的HC12編譯器使用了該堆棧。使用寄存器是用於保存冗餘推送和彈出的優化。如果沒有足夠的寄存器可用,有時可以使用堆棧。海灣合作委員會有時(很少)這樣做,這也解釋了爲什麼6812使用堆棧,因爲有很少的寄存器。只是好奇,你有什麼其他方式找到我的答案錯了? – 2011-05-20 00:53:10
如果'int z'將z壓入堆棧,並且'return z'也將z壓入堆棧,那麼機器代碼在返回後將無法重置'SP',而不會破壞返回值;在'RET'指令之前的代碼當然不能這樣做,但調用代碼需要太多關於被調用子例程的知識(除非它將'SP'的原始值存儲在寄存器中,我想)。 – 2011-05-20 02:28:21
堆棧並非真正專用於DRAM。他們是一個數據結構。它由處理器實現以緩解函數調用和遞歸。我在下面發佈了一些細節。 – 2011-05-19 03:14:30