2012-01-12 86 views
26

我的代码经常将C++字符串转换为CStrings,我想知道原始字符串是否分配在堆栈上,CString是否也会分配到堆栈上?例如:string.c_str()取消分配是否必要?

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

回答

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

将S2在栈上分配,或在人堆里?换句话说......我需要删除s2吗?

s2在堆栈上,是的。但是,它是一个指向字符的指针(在这种情况下,恰好是在s的文本内容的ASCIIZ表示中的第一个字符)。该文本本身就是s对象觉得构建该表示的地方。不过,他们喜欢实现,但std::string的关键实现选择是他们是否提供了“短字符串优化”,允许将非常短的字符串直接嵌入到s对象中,以及“Hello world”是否足够短从该优化受益:

  • 如果是这样,那么s2将指向堆内s
  • 否则分配的内存,内部s会有一个指向空闲存储/堆分配的内存,其中的“Hello world \ 0“内容将会出现,其地址将由.c_str()返回,并且s2将是该值的副本。

注意c_str()const,所以你的代码编译,你需要改变const char* s2 = ...

您不需要删除s2,no。 s2分数据仍由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(); 

...需要成为...

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>

12

c_str()返回一个指向string对象的内部缓冲区的指针 - 你永远不会free()/delete它。

只有string它指向的范围内才有效。另外,如果您调用string对象的非常量方法,则不再保证其有效。

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

(编辑基于以下注释清晰)

+0

有趣,所以,你永远不会释放char *,因为这样做会释放字符串中的内部数据? c_str()字面上保持与字符串使用的实际数据相同的地址?多次调用.c_str()会以这种方式返回相同的地址? (只是澄清,所以我知道我明白) – 2012-01-12 23:03:51

+0

@Georges这些问题的答案取决于你是否使用最新的标准或旧标准。你可能想澄清哪一个你感兴趣。 – 2012-01-12 23:05:45

+0

@ R.MartinhoFernandes更好地使用最新的标准。这将是有趣的,但要知道这个标准是多么新近 – 2012-01-12 23:06:32

0

那要看情况。如果我没有记错,CString会生成一个输入字符串的副本,所以不需要,您不需要任何特殊的堆分配例程。

4

std::string::c_str()返回const char*而不是char *。这是一个很好的迹象表明你不需要释放它。内存由实例管理(例如,参见this link中的一些细节),所以它只在字符串实例有效时有效。

+6

指向对象的const限定并不表示它不会呼叫者不需要释放。 – 2012-01-12 23:08:11

+0

@JamesMcNellis:对象为true。但是char *不完全是一个对象,只是一个普通的C类型。在普通的C语言中,常量用来表示“你不拥有这个内存”(因为'free(3)'带有一个非const指针)。 – vanza 2012-01-12 23:37:48

+0

@JamesMcNellis:这种功能有哪些例子?我认为它非常混乱,并且在返回的'const'指针的指向对象需要被调用者释放时非常清楚地评论。 – leftaroundabout 2012-01-12 23:45:11

2

s2只要s保持在范围内就会有效。它是s拥有的内存指针。见例如this MSDN documentation“该字符串具有有限的生命周期并且属于类字符串。”

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

1

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

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

我需要删除s2吗?

不,字符串对象拥有由c_str返回的字符数组对象。

现在s2会在堆上,因为它的原点在堆上?

在这种情况下,即使在进行SSO时,数据仍可能驻留在堆中。但是很少有理由动态分配std::string对象。

+0

谢谢非常详细的答案:) – 2012-01-12 23:12:34

4

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

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

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

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