1 回答

TA貢獻1735條經驗 獲得超5個贊
這個問題非常接近過于寬泛和主要基于意見的界限,但我明白你在問什么。
了解歷史上有無數不同的處理器設計和系統實現。隨著時間的推移,語言和處理器也在不斷發展。因此,任何絕對陳述實際上都是有限的,因為毫無疑問,該陳述不適用于某個系統或處理器。
一般來說,堆棧只是內存,堆棧指針只是該內存中的地址/偏移量,push/pop 與普通內存訪問的不同之處在于程序員通常不/不應該關心特定地址,但是相反,我推了五個東西,所以第三個東西離堆棧指針這么遠,為了清理我需要彈出 5 個東西,等等。但它只是一個帶有地址指針的 ram。
雖然我們認為較低編號的地址較低,較高編號的地址較高,并且期望內存的繪圖/可視化具有較低編號的地址在圖表上較低而較高地址在圖表上較高,但有時有充分的理由或有時沒有這是翻轉。在芯片上并沒有真正的上升或下降,也沒有假設內存是以某種長的物理線性二維方式布局的,這些都是簡單的可視化。
我不知道有沒有例外,但通常處理器會按地址遞增的方向執行,地址為 0x1000 的指令長度為 4 個字節,下一條指令假定為 0x1004,而不是 0xFFC。因此,讓我們假設代碼向上增長或從低地址增長到高地址。
假設我們的固件在 ram 而不是閃存中運行,我們正在談論 ram 的消耗。并從裸機的角度思考,而不是一次加載許多應用程序的操作系統。
一個程序一般會有一些代碼(通常稱為.text)、一些數據、(全局)變量等(通常稱為.data 和.bss)。堆是運行時分配的內存和堆棧。
我沒有對此進行研究,但根據我所學的內容和名稱本身,可以將堆棧視為一疊盤子或一疊記事卡。由于重力而向上生長。獨立于處理器架構,將堆??梢暬癁橄蛏显鲩L并不少見,新項目放置在舊項目之上,移除頂部項目以獲取較低項目。但這并不是那么死板,不確定它是否是 50/50,但你會經??吹剿梢暬癁橄蛳潞拖蛏显鲩L?;驇в卸褩V羔樀幕瑒哟翱诓粫趫D表中視覺移動,但數據會根據顯示方式向上或向下移動。
另請注意,該站點的名稱 Stack Overflow,該術語具有特定的含義假設......
切入正題,經典模型(后面會提到例外)是從較低的內存開始,或者讓我們假設為零,你有你的代碼、機器代碼和屬于該類別的任何其他內容。然后你有你的全局變量.data 和.bss,然后你有你的堆,最頂層是你的堆棧。堆和棧在運行時被認為是動態的。如果您從不釋放,則假定堆向上增長。所以 stack 的自然解決方案是讓它向下增長。您從最低地址開始堆,理想情況下是在其他項目(.text、.data、.bss)之上,堆棧盡可能高,以便堆棧溢出(堆棧和堆沖突,堆棧增長進入堆分配的內存)。
這種傳統模型意味著堆棧向下增長,即從高地址到低地址。許多指令集架構將推/彈出解決方案限制為這樣,使用設計的指令堆棧向下增長有例外,例如傳統的(aarch64 之前的)arm 指令(全尺寸而不是拇指)可以采用任何一種方式,因此情況下,這是編譯器作者的選擇,而不是由體系結構強制的??梢哉f,使用可以訪問內存的通用寄存器,編譯器可以選擇使用簡單的加載/存儲指令,而不是 push/pop 或等效指令,并做任何他們想做的事情。但除了可能非常有限的例外,堆棧從地址的角度向下增長。
一些架構的堆棧被埋在不可見的空間中,舊的舊芯片可能相對于今天有一個非常小的堆棧,如 16 深或 32,我們唯一的訪問是推送和彈出,僅此而已。
一些具有推送/彈出或等效功能的體系結構,例如在推送時將寫入然后調整堆棧指針或調整堆棧指針然后寫入因此對于 16 位系統要獲取所有位置你可以從 0x10000 開始你不能代表 0x0000,其他代表 0xffff 或 0xfffc,具體取決于體系結構及其工作方式等。
所以如果你想把一堆想象成一堆東西,一堆便條卡,一堆盤子,等等。那么由于重力,你會把它想象成向上生長。我在記事卡上寫一個數字,把它放在堆棧上,在記事卡上寫另一個數字,然后把它放在(推)堆棧上,取出卡片(彈出)等等。因此,由于它是 50/50 的比例,您有時會看到以這種方式可視化的堆棧,其中較高的地址位于圖表的下部,而較低的地址位于圖表的上部。
所以基于意見,這就是為什么他們以這種方式繪制圖表的原因。在一天結束時,請做好心理準備,以處理人們想象堆棧的任何方式。
為什么堆棧指針從堆棧中的最后一個地址開始?
這在經典意義上是典型的。但是在現實世界中,有一些用例,其中堆棧與其他可能受到安全功能(mmu 等)保護以免超出其空間的項目放置在不同的內存空間中。但是,堆棧指針和/或指令的正常使用是為了讓堆棧相對于所使用的內存地址向下增長,這通常是一種架構限制。所以,如果你長大了,你想從高處開始。Last address 是一種教科書式的方法,但您經常會看到人們在鏈接描述文件中分配堆??臻g,并且它在它到達的地方(有時甚至在堆或數據下方)。
這真的是所有語言中堆棧的實現方式嗎?
范圍太廣,語言本身編譯成使用指令的代碼,它的鏈接和引導程序(或操作系統)確定程序堆棧的初始值?;诙褩V羔樀闹噶畋幌拗茷橄蛳略鲩L的堆棧并不少見。如果有選擇,基于意見,我預計由于歷史原因,實施將向下(地址)增長。
這種實現堆棧的方式是否有助于避免由于堆棧溢出而出現的問題?
是的,如果我們假設堆向上增長而堆棧向下增長,那么您希望堆從可用空間的底部開始,而堆棧從頂部開始,以便在堆棧溢出發生之前提供最大的空間。
跟棧和堆在內存中的存儲方式有關系嗎?
是的,基于意見。如上所述。
如果我們從地址 $ffe6 開始,會發生什么變化?
每個“函數”都沒有真正被稱為堆棧指針的位置,這就是您不關心地址的全部要點,只關心匹配推送和彈出或可能的相對尋址,而不是絕對尋址。因此,如果 $ffe6 那么當您推送和彈出地址時,地址會變小/變大。如果 8000 美元,同樣的交易 5432 美元,同樣的交易。如果您的起始地址與教程中顯示的地址不同,一切都一樣,只是顯示的物理地址需要反映新的起點。
所以是的,堆棧的傳統/教科書視圖是后進先出。地址空間向下增長,但 50/50 是關于文本的作者如何在圖表底部或頂部使用高地址將其可視化。實際上,更高性能的指令集不僅限于嚴格的壓入和彈出,還包括相對尋址,因此當您從僅學習壓入/彈出開始時,您可以直接進入相對尋址。我將 5 個東西壓入堆棧,我可以使用 sp+offset 尋址訪問所有這些東西,有時使用基于 sp 的特殊指令。
不要為某些教程/教科書作者如何可視化堆棧、頂部或底部的更高地址而煩惱。
添加回答
舉報