函数局部变量的返回如何工作?[复制]

How does return work for a function local variable? [duplicate]

提问人:Neeraj-Kumar-Coder 提问时间:3/13/2021 最后编辑:anastaciuNeeraj-Kumar-Coder 更新时间:3/15/2021 访问量:585

问:

我对使用指针重新分级局部变量感到困惑:变量、它的地址和返回地址。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;

为什么不返回地址,尽管返回的值与第一个示例中的值相同?

第三:

#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

现在,这个方法如何返回局部变量的地址并打印其正确的值,尽管变量应该在函数返回后超出范围/被销毁?

请解释一下这些方法是如何工作的三种情况。

C 函数 指针 返回 局部变量

评论

0赞 alex01011 3/13/2021
int a -> static int a
4赞 Eugene Sh. 3/13/2021
@alex01011 使用需要充分了解它的作用。我不会断章取义地放弃这样的建议。static
0赞 alex01011 3/13/2021
它不是核物理学。只需查看文档,他就可以确切地理解它的作用。
1赞 klutt 3/13/2021
这是未定义的行为。当变量超出范围时,C 不会擦除变量的值。
1赞 Eugene Sh. 3/13/2021
@stark这实际上是一个问题。根据 port70.net/~nsz/c/c11/n1570.html#6.2.4当指针指向(或刚刚过去)的对象到达其生命周期结束时,指针的值变得不确定。

答:

1赞 Scott Hunter 3/13/2021 #1
  • Output First:返回(没有作用域)。
  • 输出二:您正在尝试返回超出范围的内容的地址。
  • 输出 3:尽管您尝试执行与以前相同的操作,但编译器未检测到问题。但是,返回的指针同样有问题。
1赞 Chuck Waggon 3/13/2021 #2
int returner(void)
{
    int a = 10;
    return a;
}

上面的函数返回一个 by 值,就返回而言,它相当于写了intreturn 10;

int *returner(void)
{
    int a = 10;
    return &a;
}

以上是他们所谓的“未定义行为”的一个例子——它有时可能会起作用,但它并不合理。返回的地址是指函数调用堆栈上用于为函数局部变量提供存储的位置。该内存不再保留用于在退货后保留,尽管它可能没有被重复使用,并且可能在该地址仍具有价值,但在之后的一段时间内,无法保证。int areturner10

第三个例子与第二个例子没有太大区别。该行为可能与第 2 种行为不同,因为堆栈分配不同,但两者都不应期望一致地工作或可移植。

0赞 Govind Parmar 3/13/2021 #3

从函数返回的是 的值,就像传递给函数的是其参数的一样。a

唯一需要担心超出范围的情况是,当您返回指向具有自动存储持续时间的局部函数变量的指针时,即:

int *badFunction ()
{
   int ret[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
   return ret;
}

在这种情况下,返回的值是指向数组的指针,但是,由于退出时超出范围,因此返回的指针显然是无效的。解决方法是使用具有存储持续时间或堆的变量。retretbadFunctionstatic

就您而言,使用普通的旧 ,您无需担心范围。int

3赞 anastaciu 3/13/2021 #4

在 C 中,返回值是按值的。

第一个代码返回 的值,即 10,这很好,它只是局部变量的副本。aa

第二个代码返回指向 的指针的值,即它的地址。碰巧的是,在函数超出范围后,该地址将无效,并且存储在该地址中的局部变量的生存期结束。编译器足够聪明,可以检测到这一点并警告您。a

第三段代码是不同的,有一个来自另一个指针的地址的赋值,编译器不会进一步检查赋值的地址是否有效,额外的间接层会抛弃编译器。

输出仍然是,但这是偶然的,它是未定义的行为,因此,这是可能的结果之一。这种结构没有标准化的行为,为了演示它,我做了一些调整。正如你在这里看到的,启用了优化。10https://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

在这两种情况下,该值都不再正确,并且行为与 不同。clanggcc