堆栈上的内存泄漏

时间:2016-12-13 14:33:47

标签: c++ memory-leaks

是否有可能通过非常糟糕的设计在C ++ 没有堆分配的情况下创建内存泄漏?

我想到的一个例子,如果不按照我的想法行事,请纠正我:

#include <iostream>
#include <string>

void WhatIsYourName()
{
  std::string name;
  std::cout << "What is your name? ";
  getline (std::cin, name);
  std::cout << "Hello, " << name << "!\n";

  WhatIsYourName();
}

int main()
{
  WhatIsYourName();
}

对我来说,看起来WhatIsYourName()正在每次调用时初始化一个新的std::string name,但该函数永远不会超出范围,因此永远不会释放内存。 是对的吗 ?或者编译器是否足够聪明,以便将来不会使用该变量,所以删除它而不会使函数超出范围?

什么样的其他不良设计会仅使用堆栈分配来创建内存泄漏?

5 个答案:

答案 0 :(得分:5)

你有一个不会停止的递归调用(没有退出条件),所以为每个函数调用创建一个新的堆栈页面,直到程序因堆栈溢出而崩溃。编译器无法优化它。

关于你的上一个问题,是否有可能在没有堆分配的情况下创建内存泄漏(并且没有这种无限递归),我相信不是,局部变量是自动释放的,这就是为什么这种类型存储持续时间称为automatic storage duration

答案 1 :(得分:4)

不可能使用具有自动存储持续时间的对象(某些人称之为此堆栈)创建内存泄漏。如果您有一个具有自动存储持续时间的变量,它将由程序自动清理。一旦范围结束,它被声明在它中被销毁并且它所拥有的任何资源被释放(只要它被正确设计)。在您的示例中,即使您由于无限递归而从未退出作用域,也不是内存泄漏。如果无限递归被删除,而是在某种情况下结束,那么你声明的所有变量都将被清除。

要获得内存泄漏,您需要创建一个具有动态存储持续时间的对象(有些人称之为堆)并且从不清理它。当您拥有具有动态存储持续时间的对象时,编译器不会为您清理它。你需要确保自己清理它。这是忘记或遗漏清理代码导致内存泄漏。

答案 2 :(得分:2)

问题比你想象的更有趣。根据定义没有内存泄漏,因为只有当a)指向已分配内存的指针丢失时(这里不是这种情况)或b)在程序正常退出之前,指针永远不会被释放,因此只能发生内存泄漏。第二种情况也不是这样,因为这个程序永远不会正常退出。

但是,有一个有趣的问题是字符串变量实际发生了什么。由于递归调用发生在函数的末尾,所以看起来代码似乎是尾调​​用优化的候选者。但是,如果检查程序集,则不会发生,因为编译器需要在从尾调用返回后调用字符串析构函数。我个人认为这是优化中的一个缺陷,因为潜在的编译器应该能够看到在调用之后不使用字符串变量并提前破坏它。

答案 3 :(得分:1)

我认为您误解了C和C ++世界中的内存泄漏。 鉴于你的情况&#34;没有堆分配&#34;,它就不可能泄漏内存。

A memory leak通常与动态内存分配相关联,然后失去解除分配的能力。 例如:

// Memory leak
int x;
int *q = new int;
q = &x; // memory leak because the "allocated" q is lost.

使用RAII而不使用new + delete进行内存管理,可以在C ++中轻松避免。

答案 4 :(得分:0)

如果编译器存在错误,则可能会有堆栈内存泄漏。由于某些模糊的原因,它“忘记了”调用局部变量的析构函数。选中GCC bug 66139