考虑以下C ++程序:
#include <cstdlib> // for exit(3)
#include <string>
#include <iostream>
using namespace std;
void die()
{
exit(0);
}
int main()
{
string s("Hello, World!");
cout << s << endl;
die();
}
通过valgrind运行它会显示这个(为简洁起见,一些输出被修剪):
==1643== HEAP SUMMARY:
==1643== in use at exit: 26 bytes in 1 blocks
==1643== total heap usage: 1 allocs, 0 frees, 26 bytes allocated
==1643==
==1643== LEAK SUMMARY:
==1643== definitely lost: 0 bytes in 0 blocks
==1643== indirectly lost: 0 bytes in 0 blocks
==1643== possibly lost: 26 bytes in 1 blocks
==1643== still reachable: 0 bytes in 0 blocks
==1643== suppressed: 0 bytes in 0 blocks
正如您所看到的,在堆上分配的26个字节丢失了的可能性。我知道std::string
类有一个12字节的结构(至少在我的32位x86 arch和GNU编译器4.2.4上)和“Hello,World!”使用null终止符有14个字节。如果我理解正确,12字节结构包含指向字符串的指针,分配的大小和引用计数(如果我在这里错了,有人会纠正我。)
现在我的问题:如何在堆栈/堆中存储C ++字符串?声明时是否存在std::string
(或其他STL容器)的堆栈对象?
P.S。我读过某些地方,valgrind 可能报告一些使用STL容器的C ++程序(以及诸如std::string
的“几乎容器”)的内存泄漏误报。我并不太担心这种泄漏,但它确实激起了我对STL容器和内存管理的好奇心。
答案 0 :(得分:11)
调用exit
“终止程序而不离开当前块,因此没有
销毁具有自动存储持续时间的任何对象“。
换句话说,泄漏与否,你真的不应该关心。当你致电exit
时,你说“关闭这个程序,我不再关心其中的任何内容。”所以停止关怀。 :)
显然,它会泄漏资源,因为你永远不会让字符串的析构函数运行,绝对不管它如何管理这些资源。
答案 1 :(得分:9)
其他人是正确的,你正在泄漏,因为你正在呼叫退出。要清楚,泄漏不是堆栈上分配的字符串,而是字符串在堆上分配的内存。例如:
struct Foo { };
int main()
{
Foo f;
die();
}
不会导致valgrind报告泄漏。
泄漏是可能的(而不是确定的)因为你有一个指向堆上分配的内存的内部指针。 basic_string负责这一点。从我机器上的标题:
* A string looks like this:
*
* @code
* [_Rep]
* _M_length
* [basic_string<char_type>] _M_capacity
* _M_dataplus _M_refcount
* _M_p ----------------> unnamed array of char_type
* @endcode
*
* Where the _M_p points to the first character in the string, and
* you cast it to a pointer-to-_Rep and subtract 1 to get a
* pointer to the header.
它们的关键是_M_p不指向堆上分配的内存的开始,它指向字符串中的第一个字符。这是一个简单的例子:
struct Foo
{
Foo()
{
// Allocate 4 ints.
m_data = new int[4];
// Move the pointer.
++m_data;
// Null the pointer
//m_data = 0;
}
~Foo()
{
// Put the pointer back, then delete it.
--m_data;
delete [] m_data;
}
int* m_data;
};
int main()
{
Foo f;
die();
}
这将报告valgrind可能发生的泄漏。如果你注释掉我移动的行m_data,valgrind将报告'仍然可以访问'。如果您取消注释我将m_data设置为0的行,您将获得明确的泄漏。
valgrind documentation提供了有关可能泄漏和内部指针的更多信息。
答案 2 :(得分:4)
当然,在exit
的堆栈帧之前s
,这个“泄漏”,你不会给s
的析构函数执行一次机会。
至于你的问题std::string
存储:不同的实现做不同的事情。有些在堆栈上分配了大约12个字节,如果字符串是12个字节或更短,则使用该字节。更长的字符串进入堆。其他实现总是进入堆。有些是引用计数和写时复制语义,有些则不是。请转向Scott Meyers的Effective STL
,第15项。
答案 3 :(得分:1)
gcc STL具有容器和字符串的私有内存池。你可以关掉它;看看valgrind FAQ
答案 4 :(得分:1)
我会避免使用exit()我认为没有真正的理由使用该调用。虽然valgrind似乎仍在运行,但不确定是否会在不清理内存的情况下立即停止进程。