在C语言中从函数返回局部变量

45
#include <stdio.h>

int foo1(void)
{
    int p;
    p = 99;
    return p;
}

char *foo2(void)
{
    char buffer[] = "test_123";
    return buffer;
}

int *foo3(void)
{
    int t[3] = {1,2,3};
    return t;
}

int main(void)
{
    int *p;
    char *s;

    printf("foo1: %d\n", foo1());
    printf("foo2: %s\n", foo2());
    printf("foo3: %d, %d, %d\n", p[0], p[1], p[2]);
    return 0;
}

当我使用gcc -ansi -pedantic -W -Wall编译这段代码时,编译器会对foo2()和foo3()发出警告:

warning: function returns address of local variable

我曾认为不允许返回本地变量,但是foo1()可以正常工作,并且返回指向本地对象和本地对象本身之间存在巨大区别。

有没有人能够解释一下这个问题?先谢谢了!

4个回答

37

这里的问题在于当你创建本地变量时,它被分配在栈上,因此一旦函数执行结束就无法使用(实现方式有所不同)。更好的方法是使用malloc()来预留非本地内存。但是,危险在于你必须使用free()释放你使用malloc()分配的所有内容,如果你忘记了,就会造成内存泄漏。


3
另一个风险是,如果您不重置(例如使用memset)已分配的内存,可能会从堆栈泄漏信息。 - jweyrich
@jweyrich 这就是calloc发挥作用的时候,当然需要保证连续分配。 - talekeDskobeDa
1
如果我将局部变量声明为静态并返回它,它仍然无法使用吗? - Abdel Aleem
2
@AbdelAleem 在那个时候,它已经进入了静态生命周期领域,因此应该是可用的。 - kelloti

28
< p >对于foo1()函数,你返回的是局部变量的副本,而不是局部变量本身。

对于其他函数,你返回的是指向局部变量的指针的副本。然而,当函数结束时,那个局部变量就被释放了,所以如果你尝试在之后引用它,就会出现问题。


这应该是被接受的答案,因为当前被接受的答案并没有真正回答问题。 - rjcarr

6
任何变量在内存中都有一些空间。指针引用该空间。局部变量占用的空间在函数调用返回时被释放,这意味着它可以和将被重用于其他事情。因此,对该空间的引用最终会指向完全不相关的东西。在C中,数组被实现为指针,所以这也适用于它们。在函数中声明的常量数组也被视为局部变量。
如果您想在创建它的函数范围之外使用数组或其他指针,则需要使用malloc来保留其空间。使用malloc保留的空间只有在显式调用free释放之前才能重新分配或重用。

3
在实践中,这意味着您需要在堆上分配返回值,而不是在栈上分配。您需要在要返回的“数组”上使用malloc或类似的函数。或者更好的方法是,将指向缓冲区和缓冲区长度的指针作为参数传递进来。 - stefan
@stefan:是的,这是一个好观点。我已经向答案中添加了更多解决此问题的建议。 - Keith Irwin
1
“C语言中的数组实现为指针”是一种过于简化的说法;虽然在某些情境下可以接受,但数组和指针之间存在重大差异(以及许多相似之处)。 - Jonathan Leffler
@Jonathan Leffler:我不知道有任何区别。它们在类型系统和引用方面都被视为相同,而引用和解引用编译成相同的汇编代码,无论是数组引用还是指针。如果你有int ip,那么ip和ip[0]是可以互换的。*(ip+1)和ip[1]也是如此。这可能只是我不知道的东西,但在C语言中,数组和指针有什么不同吗? - Keith Irwin
1
考虑以下情况:char *a ="a"; char b[] = "b"; char *c; char d[] = "d";。您可以编写:c = a;c = b;,但是您不能编写b = a;b = d;,这是数组和指针之间重要的区别。 - Jonathan Leffler
@Jonathan:啊,你说得对。所以它们在类型系统中实际上并不被视为相同的。感谢你的例子。 - Keith Irwin

0

是的,您正在返回一个数组,实际上是在幕后指向已初始化变量内容存储位置的指针地址。因此,它警告您返回这样的结果可能并不是非常有用,当您可能真正需要的是其中一个数组值时。


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