[英]C program stack
#include<stdio.h>
int* foo() {
int a = 5;
return &a;
}
void bar() {
int a = 7;
}
void foobar() {
int a = 97;
}
int main() {
int* p = foo();
bar();
foobar();
printf("%d", *p);
return 0;
}
我無法理解這種行為背后的概念。 為什么輸出始終是foobar函數中局部變量a的值?
您不能這樣做:
int* foo() {
int a = 5;
return &a; /* variable "a" is out of scope once "foo()" returns */
}
這是“未定義的行為” 。 結果可能因環境而異,因編譯器而異,甚至可能一次又一次地運行。 但這始終是“垃圾”。
這是未定義的行為,因為您返回了指向局部變量的指針,該局部變量在其功能范圍之外無效。 因此,內存中的這一點似乎可以重復使用。 永遠不要返回指向局部變量的指針!
程序具有未定義的行為,因為指針p
由函數foo
的局部變量的地址初始化
int* p = foo();
退出函數后,局部變量被銷毀並且指針無效。
ptogram始終輸出函數foobar
的局部變量的值的原因是,似乎這些函數在調用時使用相同的堆棧框架。 因此,它們的局部變量位於堆棧中的同一地址。
如果您將通過以下方式更改函數foo
int* foo() {
static int a = 5;
return &a;
}
那么程序將輸出具有靜態存儲持續時間的函數的局部變量的值。
調用函數時,編譯器提供代碼以准備堆棧,保存當前堆棧指針並為局部(自動)變量騰出空間。 這通常稱為功能序言 。 序言之后的堆棧布局或多或少:
+-----------------------------+
| Parameters if any |
+-----------------------------+
| Return address |
+-----------------------------+
| copy of ESP (stack pointer) |
+-----------------------------+
| Local variables begin here |
+-----------------------------+
| ... |
現在,如果您具有3個將開發相同布局的函數:
foo() bar() foobar()
+----------------+ +----------------+ +----------------+
| Return address | | Return address | | Return address |
+----------------+ +----------------+ +----------------+
| ESP | | ESP | | ESP |
+----------------+ +----------------+ +----------------+
| int a | | int a | | int a |
+----------------+ +----------------+ +----------------+
| ... | | ... | | ... |
如果在第一個函數foo()
獲得了變量a
的地址,則在調用bar()
和foobar()
時將重用相同的地址。 在調用之后訪問a
,您會發現foobar()
寫入的最后一個值。
如果您以這種方式更改功能:
#include<stdio.h>
int* foo() {
int a = 5;
return &a;
}
int* bar() {
int a;
int b = 7;
return &b;
}
int* foobar() {
int a;
int b;
int c = 97;
return &c;
}
int main() {
int* p1 = foo();
int* p2 = bar();
int* p3 = foobar();
printf("%d %d %d", *p1, *p2, *p3);
return 0;
}
令人驚訝的是,您將讀取所有值。 現在的情況是:
foo() bar() foobar()
+----------------+ +----------------+ +----------------+
| Return address | | Return address | | Return address |
+----------------+ +----------------+ +----------------+
| ESP | | ESP | | ESP |
+----------------+ +----------------+ +----------------+
| int a | | int a | | int a |
+----------------+ +----------------+ +----------------+
| ... | | int b | | int b |
+----------------+ +----------------+ +----------------+
| ... | | ... | | int c |
+----------------+ +----------------+ +----------------+
| ... | | ... | | ... |
順便說一句,這屬於未定義行為的大家族,最重要的是錯誤 。 自動變量的壽命僅限於其范圍,並且不得在外部使用。
無論如何,堆棧上值的行為通常是穩定的,因為內存管理器會保留堆棧頁的數據(這就是為什么將未初始化的局部變量用作隨機值沒有意義),但是在將來的體系結構設計中,MM可以丟棄未使用的內存並且不要保存它,從而實際上未定義這些存儲位置的內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.