2012-01-12 83 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()所指的內存都不屬於您。你不應該釋放它。你甚至不應該知道它在哪裏分配。