return 如何对函数局部变量起作用? [复制]
Posted
技术标签:
【中文标题】return 如何对函数局部变量起作用? [复制]【英文标题】:How does return work for a function local variable? [duplicate] 【发布时间】:2021-06-10 18:17:21 【问题描述】:我对重新分级局部变量的return
感到困惑:变量、它的地址和使用指针的返回地址。
第一:
#include <stdio.h>
int returner(void);
int main(void)
printf("The value I got is = %d\n", returner());
return 0;
int returner(void)
int a = 10;
return a;
输出:
The value I got is = 10
返回的局部变量虽然在函数返回后应该超出范围,但它是如何工作的?
第二:
#include <stdio.h>
int *returner(void);
int main(void)
int *pointer = returner();
printf("The value I got is = %d\n", *pointer);
return 0;
int *returner(void)
int a = 10;
return &a;
输出:
Test.c: In function 'returner':
Test.c:15:12: warning: function returns address of local variable [-Wreturn-local-addr]
15 | return &a;
为什么地址没有返回,虽然返回的值和First sample一样?
第三:
#include <stdio.h>
int *returner(void);
int main(void)
int *pointer = returner();
printf("The value I got is = %d\n", *pointer);
return 0;
int *returner(void)
int a = 10;
int *ptr = &a;
return ptr;
输出:
The value I got is = 10
现在,这个方法如何返回局部变量的地址并打印它的正确值,尽管该变量应该在函数返回后超出范围/被销毁?
请解释这三种方法的工作原理。
【问题讨论】:
int a
-> static int a
@alex01011 使用static
需要完全了解它的作用。我不会断章取意地放弃这样的建议。
它不是核物理。简单地看一下文档,他就能准确地理解它的作用。
这是未定义的行为。当变量超出范围时,C 不会擦除变量的值。
@stark 实际上是一个问题。根据port70.net/~nsz/c/c11/n1570.html#6.2.4:当指针指向(或刚刚过去)的对象到达其生命周期结束时,指针的值变得不确定。
【参考方案1】:
在 C 中,返回是按值。
第一个代码返回 a
的值,即 10,这很好,它只是局部变量 a
的副本。
第二个代码返回指向a
的指针的值,即它的地址。碰巧在函数超出范围后该地址将无效,并且存储在该地址中的局部变量的生命周期结束。编译器智能足以检测到这一点并警告你。
第三个代码不同,有一个来自另一个指针的地址分配,编译器不会进一步检查分配的地址是否有效,额外的间接层会使编译器关闭。
输出仍然是10
,但这是偶然的,它是undefined behavior,因此,这是可能的结果之一。这种构造没有标准化的行为,为了演示它,我稍微调整了一下。正如您在此处看到的 https://godbolt.org/z/eKerdM,启用了优化 -O3
。
使用clang
,程序会输出一些随机值:
The value I got is = -1313555320
并且不会产生任何警告。
而gcc
进一步验证并检测到有问题的分配:
<source>: In function 'returner': <source>:16:12: warning: function returns address of local variable [-Wreturn-local-addr] 16 | return ptr; | ^~~ <source>:14:9: note: declared here 14 | int a = 10; | ^
程序输出0:
The value I got is = 0
在这两种情况下,值都不再正确,clang
的行为与gcc
不同。
【讨论】:
【参考方案2】:函数返回的是a
的值,就像传递给函数的是其参数的值一样。
您唯一需要担心超出范围的情况是当您返回指向具有自动存储持续时间的局部函数变量的指针时,即:
int *badFunction ()
int ret[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ;
return ret;
在这种情况下,返回的值是指向数组ret
的指针,但是由于ret
在badFunction
退出时超出范围,因此返回的指针显然是无效的。解决方法是使用具有static
存储持续时间或堆的变量。
在您的情况下,使用普通的旧 int
,您无需担心范围。
【讨论】:
【参考方案3】:int returner(void)
int a = 10;
return a;
上述函数按值返回一个int
,就返回而言,相当于写了return 10;
int *returner(void)
int a = 10;
return &a;
以上是他们所谓的“未定义行为”的一个示例 - 它有时可能会起作用,但它并不合理。返回的地址是指函数调用堆栈上用于为函数局部变量提供存储的位置。在returner
返回后,该内存不再保留用于保存int a
,尽管它可能没有被重用,并且在该地址可能仍然具有10
的值,一段时间后,无法保证。
第三个例子和第二个没有太大区别。行为可能与第二种不同,因为堆栈分配不同,但不应期望两者都能一致地工作或可移植。
【讨论】:
【参考方案4】: 输出优先:返回值(没有范围)。 第二个输出:您正试图返回确实超出范围的东西的地址。 第三个输出:尽管您尝试执行与之前相同的操作,但编译器并未检测到问题。然而,返回的指针同样有问题。【讨论】:
以上是关于return 如何对函数局部变量起作用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章