[英]C++: How does the compiler know how much memory to allocate for each stack frame?
在這里的第一個答案中,提到了以下關於C ++中的堆棧內存:
調用函數時,會在堆棧頂部為區域變量和一些簿記數據保留一個塊。
這在頂層是完全有意義的,並且讓我好奇在給出這個問題的上下文時,在分配這個內存時,智能編譯器是如何分配這個內存的:因為大括號本身不是C中的堆棧框架(我認為這有用)對於C ++也是如此),我想檢查編譯器是否在單個函數內基於變量范圍優化保留內存。
在下面我假設在函數調用之前堆棧看起來像這樣:
--------
|main()|
-------- <- stack pointer: space above it is used for current scope
| |
| |
| |
| |
--------
然后調用函數f()
后面的以下內容:
--------
|main()|
-------- <- old stack pointer (osp)
| f() |
-------- <- stack pointer, variables will now be placed between here and osp upon reaching their declarations
| |
| |
| |
| |
--------
例如,給定此功能
void f() {
int x = 0;
int y = 5;
int z = x + y;
}
據推測,這只會為記賬分配3*sizeof(int)
+一些額外的開銷。
但是,這個功能怎么樣:
void g() {
for (int i = 0; i < 100000; i++) {
int x = 0;
}
{
MyObject myObject[1000];
}
{
MyObject myObject[1000];
}
}
忽略編譯器優化可能會忽略上面的很多東西,因為它們什么都不做,我對第二個例子中的以下內容感到好奇:
for
循環:堆棧空間是否足夠大以適應所有100000個整數? 1000*sizeof(MyObject)
或2000*sizeof(MyObject)
? 通常:在調用某個函數之前,在確定新堆棧幀需要多少內存時,編譯器是否會考慮變量范圍? 如果這是特定於編譯器的,那么一些着名的編譯器如何做呢?
編譯器將根據需要分配空間(通常用於函數開頭的所有項),但不會為循環中的每次迭代分配空間。
例如,Clang生成的內容為LLVM-IR
define void @_Z1gv() #0 {
%i = alloca i32, align 4
%x = alloca i32, align 4
%myObject = alloca [1000 x %class.MyObject], align 16
%myObject1 = alloca [1000 x %class.MyObject], align 16
store i32 0, i32* %i, align 4
br label %1
; <label>:1: ; preds = %5, %0
%2 = load i32, i32* %i, align 4
%3 = icmp slt i32 %2, 100000
br i1 %3, label %4, label %8
; <label>:4: ; preds = %1
store i32 0, i32* %x, align 4
br label %5
; <label>:5: ; preds = %4
%6 = load i32, i32* %i, align 4
%7 = add nsw i32 %6, 1
store i32 %7, i32* %i, align 4
br label %1
; <label>:8: ; preds = %1
ret void
}
這是以下結果:
class MyObject
{
public:
int x, y;
};
void g() {
for (int i = 0; i < 100000; i++)
{
int x = 0;
}
{
MyObject myObject[1000];
}
{
MyObject myObject[1000];
}
}
因此,正如您所看到的, x
僅分配一次,而不是100000次。 因為在任何給定時間只存在其中一個變量。
(編譯器可以為x
和第二個myObject[1000]
重用myObject[1000]
的空間 - 並且可能會為優化的構建重用該空間,但在這種情況下,它也會完全刪除這些變量,因為它們沒有被使用,所以它不會很好地顯示)
在現代編譯器中,該函數首先轉換為流程圖。 在流程的每一個弧形,編譯器知道多少個變量是活的 -也就是拿着可見值。 其中一些將存在於寄存器中,而對於其他寄存器,編譯器將需要保留堆棧空間。
隨着優化器進一步參與,事情會變得更復雜,因為它可能不希望移動堆棧變量。 這不是免費的。
盡管如此,編譯器最終還是准備好了所有的匯編操作,並且可以計算使用了多少個唯一的堆棧地址。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.