#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的值?
答案 0 :(得分:3)
这是未定义的行为,因为您返回指向局部变量的指针,该局部变量在其函数范围之外无效。因此,内存中的这一点可能会被重用,因为它似乎就在这里。永远不要返回指向局部变量的指针!
答案 1 :(得分:3)
你不能这样做:
int* foo() {
int a = 5;
return &a; /* variable "a" is out of scope once "foo()" returns */
}
这是"undefined behavior"。结果可能不同于环境到环境,编译器到编译器,甚至运行运行。但它总是&#34;垃圾&#34;。
答案 2 :(得分:2)
该程序未定义bahaviour,因为指针p
由函数foo
int* p = foo();
退出函数后,局部变量被破坏,指针无效。
你的ptogram总是输出函数foobar
的局部变量的值的原因是它们在被调用时似乎使用相同的堆栈帧。因此,它们的局部变量放在堆栈中的相同地址。
如果你将以下列方式更改foo函数
int* foo() {
static int a = 5;
return &a;
}
然后程序将输出具有静态存储持续时间的函数的局部变量的值。
答案 3 :(得分:1)
当调用函数时,编译器提供准备堆栈的代码,保存当前堆栈指针并为本地(自动)变量腾出空间。这通常称为功能序言。序言后的堆栈布局或多或少:
+-----------------------------+
| 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 |
+----------------+ +----------------+ +----------------+
| ... | | ... | | ... |
如果您在第一个函数a
中获得了变量foo()
的地址,则当您致电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可以丢弃未使用的内存并且不保存它,从而有效地定义了这些内存位置的内容。