众所周知,局部变量具有局部范围和生命周期。请考虑以下代码:
int* abc()
{
int m;
return(&m);
}
void main()
{
int* p=abc();
*p=32;
}
这给了我一个函数返回局部变量地址的警告。 我认为这是理由: 一旦abc()完成,就会释放本地可验证的m。所以我们在主函数中取消引用了无效的内存位置。
但是,请考虑以下代码:
int* abc()
{
int m;
return(&m);
int p=9;
}
void main()
{
int* p=abc();
*p=32;
}
我在这里收到同样的警告。但我想m返回时仍会保留其生命周期。怎么了?请解释错误。我的理由是错的吗?
答案 0 :(得分:10)
首先,请注意永远不会达到int p=9;
,因此您的两个版本在功能上是相同的。程序将为m
分配内存并返回该内存的地址; return语句下面的任何代码都是不可缓存的。
其次,在函数返回后,实际上并没有取消分配局部变量m
。相反,程序会考虑内存可用空间。该空间可能用于其他目的,或者它可能保持未使用状态并永远保持其旧值。因为在abc()
函数退出后您无法保证内存会发生什么,所以不应尝试以任何方式访问或修改它。
答案 1 :(得分:8)
一旦遇到return关键字,控制就会传回给调用者,被调用的函数超出范围。因此,所有局部变量都从堆栈中弹出。所以你的第二个例子中的最后一个陈述是无关紧要的,警告是合理的
答案 2 :(得分:3)
逻辑,从函数返回时m
不再存在,并且一旦函数退出,对它的任何引用都无效。
物理上,图片有点复杂。 m
占用的内存单元肯定仍在那里,如果你在其他任何东西有机会写入之前访问这些单元格,它们将包含在函数中写入它们的值,所以在适当的情况下,您可以在m
返回后阅读p
到abc
中存储的内容。 不要依赖这种可重复的行为;这是编码错误。
从语言标准(C99):
6.2.4对象的存储持续时间
...
2对象的生存期是存储期间程序执行的一部分 保证为它保留。存在一个对象,具有一个常量地址, 25)并保留 它在其整个生命周期中的最后存储值。 26) 如果一个对象被引用到它的外面 一生,行为是不确定的。当指针变为不确定时,指针的值变得不确定 它指向的对象到达其生命周期的末尾。
25)术语“常量地址”意味着指向对象的两个指针可能不同 时间会比较平等。在两次不同的执行期间,地址可能不同 节目。
26)对于易失性对象,最后一个商店不需要在程序中显式。
强调我的。基本上,你正在做一些语言定义明确地称为未定义行为的东西,这意味着编译器可以自由地以任何方式处理这种情况。它可以发出诊断(您的编译器正在执行),它可以转换代码而无需发出诊断,它可以在此时停止转换等。
答案 3 :(得分:2)
退出函数时,唯一可以使m仍然有效的内存(与代码保持最大相似性)的方法是在前面添加静态关键字
int* abc()
{
static int m;
m = 42;
return &m;
}
返回后的任何事情都是"死分支"那将永远不会被执行。
答案 4 :(得分:-1)
int m
应该在本地可见。您应该将其创建为int* m
并直接返回。