string.c_str()需要解除分配吗?

时间:2012-01-12 22:58:59

标签: c++ string memory-management stack heap

我的代码经常将C ++字符串转换为CStrings,我想知道原始字符串是否在堆栈上分配,CString是否也会在堆栈上分配?例如:

string s = "Hello world";
char* s2 = s.c_str();

会在堆栈上还是在堆中分配s2?换句话说,我是否需要删除s2

相反,如果我有这段代码:

string s = new string("Hello, mr. heap...");
char* s2 = s.c_str();

s2现在会在堆上,因为它的起源是在堆上吗?

澄清一下,当我询问堆上是否有s2时,我知道指针在堆栈上。我问的是它指向的内容是在堆还是堆栈上。

7 个答案:

答案 0 :(得分:36)

string s = "Hello world";
char* s2 = s.c_str();
  

s2会在堆栈上还是在堆中分配?换句话说......我需要删除s2吗?

不,不要 delete s2

如果上面的代码在函数内部,则

s2在堆栈中;如果代码位于全局或命名空间范围,则s2将位于某个静态分配的动态初始化数据段中。无论哪种方式,它都是指向字符的指针(在这种情况下恰好是'H'的文本内容的ASCIIZ表示中的第一个s字符。该文本本身就是s对象感觉构建该表示的任何地方。实现被允许这样做,但他们喜欢,但std::string的关键实现选择是它是否提供“短字符串优化”,允许非常短的字符串直接嵌入s对象和"Hello world"是否足够短以从优化中受益:

  • 如果是,那么s2将指向s内的内存,这将按照s2上面的说明进行堆叠或静态分配
  • 否则,在s内会有一个指向动态分配(自由存储/堆)内存的指针,其中会出现{Hello,\ _0}内容由.c_str()返回的内容,并且s2将是该指针值的副本。

请注意,c_str()const,因此要编译代码,您需要更改为const char* s2 = ...

不得delete s2 s2对象仍由s点所拥有和管理的数据将因const或{{1}的非s方法调用而失效超出范围。

s
  

s2现在会在堆上,因为它的起源是在堆上吗?

此代码无法编译,因为string s = new string("Hello, mr. heap..."); char* s2 = s.c_str(); 不是指针而且字符串没有像s这样的构造函数。您可以将其更改为:

string(std::string*)

...或...

string* s = new string("Hello, mr. heap...");

后者会造成内存泄漏而没有用处,所以让我们假设前者。然后:

string s = *new string("Hello, mr. heap...");

......需要成为......

char* s2 = s.c_str();
  

s2现在会在堆上,因为它的起源是在堆上吗?

是。在所有场景中,特别是如果char* s2 = s->c_str(); 本身在堆上,则:

  • 即使在s s内有一个短字符串优化缓冲区,c_str()产生指针,它也必须在堆上,否则
  • 如果s使用指向更多内存的指针来存储文本,那么该内存也将从堆中分配。

但同样,即使确定s2指向堆分配的内存,您的代码也不需要释放该内存 - 它将在删除s时自动完成:

string* s = new string("Hello, mr. heap...");
const char* s2 = s->c_str();
...use s2 for something...
delete s;   // "destruct" s and deallocate the heap used for it...

当然,通常最好只使用string s("xyz");,除非您需要超出本地范围的生命周期,否则为std::unique_ptr<std::string>std::shared_ptr<std::string>

答案 1 :(得分:15)

c_str()返回指向string对象中内部缓冲区的指针 - 您永远不会free() / delete

只有当它指向的string在范围内时才有效。此外,如果您调用string对象的非const方法,则不再保证其有效。

http://www.cplusplus.com/reference/string/string/c_str/

(根据以下评论编辑清晰度)

答案 2 :(得分:4)

std::string::c_str()会返回const char*,而非char *。这是一个很好的迹象,表明你不需要释放它。内存由实例管理(例如,请参阅this link中的一些详细信息),因此它仅在字符串实例有效时才有效。

答案 3 :(得分:4)

首先,即使您的原始字符串未在堆栈中分配,您似乎也相信。至少不完全。如果将string s声明为局部变量,则只有string对象本身“在堆栈中分配”。该字符串对象的受控序列在其他地方分配。您不应该知道它的分配位置,但在大多数情况下,它是在堆上分配的。即第一个示例中"Hello world"存储的实际字符串s通常在堆上分配,无论您在何处声明s

其次,关于c_str()

在C ++(C ++ 98)的原始规范中,c_str通常返回指向某处分配的独立缓冲区的指针。同样,你不应该知道它的分配位置,但一般情况下它应该在堆上分配。大多数std::string的实现都确保它们的受控序列始终为零终止,因此它们的c_str返回了一个指向受控序列的直接指针。

在新的C ++规范(C ++ 11)中,现在要求c_str返回指向受控序列的直接指针。

换句话说,在一般情况下,即使对于本地c_str对象,std::string的结果也将指向堆分配的内存。你的第一个例子与你在这方面的第二个例子不同。但是,在任何情况下c_str()指向的内存都不归您所有。你不应该解除它。你甚至不应该知道它的分配地点。

答案 4 :(得分:2)

只要s2仍在范围内,

s就会有效。它是s拥有的内存指针。参见例如this MSDN documentation“字符串的生命周期有限,并且由类字符串拥有。”

如果要在函数内部使用std::string作为字符串操作的工厂,然后返回c样式的字符串,则必须为返回值分配堆存储。使用mallocnew获取空间,然后复制s.c_str()的内容。

答案 5 :(得分:1)

  

s2会在堆栈上还是在堆中分配?

可以在任何一个。例如,如果std::string类执行小型字符串优化,则数据将在堆栈(如果其大小低于SSO阈值)时驻留在堆栈上,否则将驻留在堆上。 (这都是假设std::string对象本身在堆栈上。)

  

我需要删除s2吗?

不,c_str返回的字符数组对象归字符串对象所有。

  

s2现在会在堆上,因为它的起源是在堆上吗?

在这种情况下,无论如何数据都可能存在于堆中,即使在进行SSO时也是如此。但是很少有理由动态分配std::string对象。

答案 6 :(得分:0)

这取决于。如果我没记错的话,CString会复制输入字符串,所以不,你不需要任何特殊的堆分配例程。