是否需要string.c_str()解除分配

Is string.c_str() deallocation necessary?

本文关键字:str 解除分配 string 是否      更新时间:2023-10-16

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

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是否在堆上时,我知道指针在堆栈上。我在问它指向的是在堆上还是在堆栈上。

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

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

不,不要delete s2

如果上述代码在函数内部,则s2在堆栈上;如果代码在全局或命名空间范围内,则s2将处于某个静态分配的动态初始化数据段中。无论哪种方式,它都是指向某个字符的指针(在本例中,该字符恰好是s的文本内容的以null结尾的字符串表示中的第一个'H'字符)。文本本身就在s对象想要构建该表示的任何地方。实现可以随心所欲地这样做,但std::string的关键实现选择是它是否提供";短串优化";允许非常短的字符串直接嵌入到s对象中,以及"Hello world"是否足够短以从该优化中受益:

  • 如果是这样,则s2将指向s内部的内存,该内存将像上面针对s2所解释的那样进行堆栈或静态分配
  • 否则,在s内部将有一个指向动态分配(自由存储/堆)存储器的指针;你好世界\0"地址由.c_str()返回的内容将出现,并且s2将是该指针值的副本

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

您不能delete s2s2指向的数据仍然由s对象拥有和管理,对s的非const方法的任何调用或超出范围的s都将使其无效。

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

s2现在会在堆上吗,就像它的原点在堆上一样?

这段代码不编译,因为s不是指针,字符串也没有像string(std::string*)那样的构造函数。您可以将其更改为:

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

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

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

char* s2 = s.c_str();

需要成为。。。

const char* s2 = s->c_str();

s2现在会在堆上吗,就像它的原点在堆上一样?

是的。在所有场景中,特别是如果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>

c_str()返回指向string对象中内部缓冲区的指针。你从来没有free()/delete它。

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

请参阅std::string::c_str

首先,即使是原始字符串也不会像您所相信的那样在堆栈上分配。至少不完全是这样。如果您的string s被声明为局部变量,那么只有string对象本身是"在堆栈上分配的"。该字符串对象的受控序列被分配到其他地方。您不应该知道它被分配到哪里,但在大多数情况下,它是在堆上分配的。也就是说,第一个例子中s存储的实际字符串"Hello world"通常是在堆上分配的,而不管您在哪里声明s

其次,关于c_str()

在C++(C++98)的原始规范中,c_str通常返回一个指向某个地方分配的独立缓冲区的指针。同样,您不应该知道它被分配到哪里,但在一般情况下,它应该在堆上分配。std::string的大多数实现都确保它们的受控序列总是零终止的,所以它们的c_str返回了一个指向受控序列的直接指针。

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

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

std::string::c_str()返回const char*,而不是char *。这很好地表明你不需要释放它。内存是由实例管理的(例如,请参阅此链接中的一些详细信息),因此只有当字符串实例有效时,它才有效。

只要s仍在范围内,则s2将有效。它是指向s拥有的内存的指针。例如,请参阅MSDN文档:"该字符串的生存期有限,并且由类字符串所有"

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

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

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

我需要删除s2吗?

否,c_str返回的字符数组对象为字符串对象所有。

s2现在会在堆上吗,就像它的原点在堆上一样?

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

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