返回指向自动变量的指针

11

假设有以下函数:

char *getp()
{
    char s[] = "hello";
    return s;
}

由于这个函数返回一个指向函数内部的局部变量的指针,会导致内存泄漏吗?

P.S. 我仍在学习C语言,所以我的问题可能有点幼稚...

[更新]
那么,如果你想返回一个新的char[]数组(比如子字符串函数),你应该返回什么?应该返回指向外部变量的指针吗?也就是说,一个不是函数内部局部变量的char[]

8个回答

27

它不会导致内存泄漏,而是会导致悬空引用。局部变量在堆栈上分配并且一旦超出作用域就会被释放。因此,当函数结束时,您返回的指针不再指向您拥有的内存。这不是内存泄漏(内存泄漏是指您分配一些内存但没有释放它)。

[更新]: 为了能够返回在函数中分配的数组,您应该在堆(heap)中分配它,而不是在栈(stack)中分配,例如:

char *test() {
    char* arr = malloc(100);
    arr[0] = 'M';
    return arr;
}

如果在使用完内存后,调用函数中没有使用free释放内存,那么就会发生内存泄漏。


arr[0] = 'M'; 赋值是必要的吗?还是只是为了演示对 arr 的非平凡操作? - user1717828
@user1717828 后者。 - Mehrdad Afshari

13
不会发生内存泄漏,因为在getp()结束后它被销毁;但会导致未定义行为,因为现在你有一个指向不再包含期望数据的内存区域的指针,而这个区域可能被其他人重用。如果在堆上存储该数组并且没有执行free()函数,则会发生内存泄漏。
char* getp(){

   char* p = malloc(N);
   //do stuff to p
   return p;
}

int main(){
    char* p = getp();
    //free(p) No leak if this line is uncommented
    return 0;
}

这里,p没有被销毁是因为它不在栈中,而是在堆中。然而,一旦程序结束,已分配的内存未被释放,引起了内存泄漏(即使这是在进程死亡后完成的)。

[更新]

如果你想从函数返回一个新的C字符串,你有两个选项。

  • 将其存储在堆中(如上面的示例或像这个实际示例,它返回一个重复的字符串);
  • 传递一个缓冲区参数

例如:

    //doesnt exactly answer your update question, but probably a better idea.
    size_t foo (const char* str, size_t strleng, char* newstr);

在这里,您需要在调用foo函数之前为newstr分配内存(可以是堆栈或堆)。在这种特定情况下,它将返回newstr中字符的数量。


8

这不是内存泄漏,因为内存被正确释放了。

但这是一个bug。 你有一个指向未分配内存的指针。这被称为悬空引用,在C语言中是常见错误源。 结果是未定义的。 直到运行时尝试使用该指针时,才会看到任何问题。


3

自动变量在函数调用结束时被销毁,您不能返回它们的指针。你所做的可以描述为“返回一个指向曾经持有s的内存块的指针,但现在未使用(但可能仍然有一些内容),并且很快将被完全填充其他内容”。


2

它不会导致内存泄漏,但会导致未定义的行为。这种情况特别危险,因为指针将指向程序堆栈中的某个位置,如果您使用它,将访问随机数据。这样的指针在写入时还可以用于破坏程序安全性并使其执行任意代码。


2

还没有人提到你可以通过告诉编译器你想要数组 "s" 具有 "静态存储期" 的方式来使这个构造有效(这意味着它的生命周期与程序一样长,就像全局变量一样)。你可以使用关键字 "static" 来实现:

char *getp()
{
    static char s[] = "hello";
    return s;
}

现在的问题是,现在只有一个s的实例,被每个getp()函数调用所共享。在你编写的函数中,这并不重要。但在更复杂的情况下,它可能无法达到你想要的效果。
PS:通常的局部变量具有所谓的“自动存储期”,这意味着当函数被调用时,变量的新实例被创建,并且在函数返回时消失。有一个对应的关键字“auto”,但如果你不使用“static”,它是隐含的,因此在实际代码中几乎看不到它。

static 引用存储在堆上吗? - Andreas Grech
1
不,它们在程序启动时被加载/创建,因此它们的内存通常以类似于保存程序文本本身的内存的方式分配。 - caf

1

我在将代码放入调试器并观察反汇编和内存窗口后,删除了我的早期答案。

问题中的代码无效并返回指向栈内存的引用,该内存将被覆盖。

然而,这个稍微不同的版本返回对固定内存的引用,并且工作正常:

char *getp()
{
    char* s = "hello";
    return s;
}

0

s 是一个栈变量 - 在函数结束时它会自动取消引用。然而,你的指针将不再有效,并且可能会引用任何时刻都可能被覆盖的内存区域。


它尚未解除引用,但已不再在堆栈上分配。 - Corey Ross

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