C语言中,局部栈变量何时被“释放”?

4

本地变量至少(且最多)存在于函数内。然而,块级作用域变量在块外但在同一函数中的情况下,我能保留并使用它们的地址吗?这段代码有效吗?

#include <stdio.h>

int main()
{
   char *f;
   if (1)
   {
       char q[] = "123";
       f = q;   
   }

   printf ("%s\n", f);   
   return 0;
}

事实上,无论是gcc -ansi -pedantic还是valgrind都没有对此提出异议,但我能在跨平台和跨编译器中使用它吗?在我看来似乎不行,但有什么工具可以显示错误呢?
附注:我到底应该使用static吗?这可能是一个适当的解决方案,但在我看来似乎不是线程安全的解决方案?

3
这个副本在这里并不是一个很好的答案。问题是关于函数内作用域而不是从另一个函数传回值。虽然同样无效,但这个问题中示例无效的原因比简单返回指向已过期堆栈帧的指针更加微妙。 - Persixty
1个回答

9
不,你不能这样做。具有自动存储期的变量的生命周期设置为封闭块。你最终得到的是一个悬空引用,使用它是未定义的行为。
参考链接:[C11 §6.2.4 ¶2] 对象的生命周期是程序执行期间为其保留存储空间的部分。对象存在,并且在其生命周期内保留其最后存储的值和常量地址。如果在生命周期外引用对象,则行为未定义。指针的值在其所指向的对象(或刚刚超过该对象)达到生命周期结束时变得不确定。
参考链接:[C11 §6.2.4 ¶6] 对于这样的对象(具有自动存储期),如果它没有变长数组类型,则其生命周期从与其关联的块输入开始,直到以任何方式结束该块的执行。

1
我在我的电脑上检查了这段代码,输出为“123”。说它是未定义行为意味着PC可以用它喜欢的任何东西替换该数据(并相应地更改权限)吗? - CIsForCookies
2
@CIsForCookies - 说它是未定义行为意味着C标准不要求编译器产生一致正确的代码,甚至不要求编译器首先就能产生正确的代码。 - StoryTeller - Unslander Monica
1
@CIsForCookies 这是UB,因为"123"被放置在堆栈上作为局部变量。当作用域完成时,堆栈指针会更改其位置,而不清除不再使用的堆栈,因为这将浪费CPU时间。当您调用重用此处未使用的“123”所在的堆栈的不同函数时,UB将出现。这就是为什么应该初始化局部变量(和指针)的原因。 - Rogus
谢谢,我了解UB的问题,但我仍然想知道为什么gcc、valgrind(以及顺便说一句cppcheck)没有抱怨。 - Nick
@Nick - UB 意味着所有的赌注都取消了,取决于平台,它可能看起来能够工作,或者失败得很惨。一个几乎与你发布的代码示例完全相同的样本,在我们开始在新的目标 Arch/OS 组合上使用 GCC 构建时,在我的店里崩溃了。我会看看是否能为您找到更多细节(具体细节此刻令我困扰)。 - StoryTeller - Unslander Monica
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接