我正在解決一個問題,我必須在字串前面多次添加一個字符,所以我只使用
string l ="";
char c = 'x';
l = c l;
但是當我運行它時,它顯示超出了記憶體限制?相反,當我使用
string l ="";
char c = 'x';
l = c;
reverse(l.begin(),l.end());
它編譯成功。我想知道為什么會這樣?
uj5u.com熱心網友回復:
正如其他人提到的那樣,在字串的前面添加一個 char 將創建一個新字串并每次復制,而在后面添加一個 char 會以更大的步驟增加容量,并且只是偶爾復制。
但兩種方式都使用 <= 2N 記憶體。兩種方式使用的記憶體差別不大。添加到前面的問題是時間,而不是空間。
測驗可能注意到的是空閑記憶體的碎片。在第一種情況下,libc 將為字串 1 - N 的每個大小分配一塊記憶體,并立即再次釋放它。但是用于較小字串的塊不能用于較大的字串,除非兩個相鄰的小字串被釋放并合并到一個更大的可重用記憶體塊中。對于最后一步,您有一個大小為 N-1 和大小為 N 的字串。如果 malloc 僅使用單個堆,那么最好的情況是您需要 3N 記憶體,最壞的情況是您有一個大小為 N-1 的空閑記憶體塊,字串大小為 N-1,大小為 N-1 的空閑塊,大小為 N 的字串,大小為 N-1 的空閑塊。所以整體3N - 5N記憶體。
但是 libc 可能有一個優化的 malloc,它使用不同的記憶體池用于不同的分配大小(8、16、32、64,...位元組)。然后分配給較小字串的塊將永遠不會被重新用于較大的字串,最終您會得到 2 個大小為 8、16、32、64 的塊,......每個。或 2N log_2 N 的不可用記憶體。盡管在較大尺寸(頁面尺寸的倍數)下,malloc 將 mmap 和 munmap 塊所需的字串并顯示 0 開銷。但是對于較小的 N,您很容易以 >20N 的記憶體使用量告終。
對于l = candreverse情況,存在相同的問題,但是隨著字串以更大的步長增長,您的分配要少得多。對于簡單的 malloc,您可能仍然需要 3-4N 記憶體,而對于優化的 malloc,您最終可能只需要 N log_2 N 記憶體使用(假設字串大小翻倍)。或者 10N,第一種方法有 20N。
總結:這兩種方法都可以使用大約相同的記憶體運行,但是無法重用已釋放記憶體或效率低下會對分配的總記憶體產生很大影響。殺死你的是記憶體系統的開銷,而不是字串使用的記憶體。
如果您真的想最小化記憶體使用量reserve,請在字串上使用以使其在開始時分配字串的最終大小。然后添加所有字符并最后反轉它。那么你真的只需要N個記憶體。
uj5u.com熱心網友回復:
c l生成一個新的std::string(臨時)物件,該物件包含新的字串內容,這意味著舊字串的內容以 . 為前綴c。
現在,這個std::string物件應該在哪里存盤字串資料?它不能重用 的記憶體l,因為那時我們還不知道該字串將被分配給l。一般來說,我們可能希望重用l舊的字串內容重用 。
所以這個臨時必須為整個(前綴)字串分配記憶體并將內容復制到其中。
當然,然后我們將臨時字串分配給l并且在這種情況下(因為臨時字串之后將不再可用)l可以重用臨時物件使用的記憶體。
編譯器可能會意識到這兩個步驟最終將如何只得到一個字串物件,并且可能會跳過額外的分配,但我認為這通常不太可能。
使用l = c;,不會創建額外的字串物件。相反,您直接附加到l. 現在可能是l沒有足夠的記憶體保留來追加額外的內容c。在這種情況下,它也可能會重新分配記憶體并將原始字串內容復制到新的更大的分配中。但這只會發生在特定大小的l. 如果你像這樣反復添加字符,那么這種情況只會偶爾發生,標準庫重新分配記憶體所使用的策略將使時間成本只有一個常數因素。但是,您仍然可能很幸運,最大記憶體沒有超過限制。
如果需要重新分配,則顯示的兩個代碼片段應使用大致相同的最大記憶體(在此特定步驟中)。
當然,您的第二個示例會導致保留字串。我認為您可能也打算在附加之前將其反轉c。
不過你可以繼續使用c l。您應該確保l在該操作之后表明不再需要的內容(因為無論如何l都會被結果替換)。c l這是通過以下方式完成的std::move:
// for std::move
#include<utility>
/*...*/
l = c std::move(l);
這應該與第二種情況的記憶體使用量大致相同,但不需要將所有字符移動兩次以反轉兩次。
或者,正如@TedLyngmo 在評論中提到的那樣,在開頭附加字符的最直接方法可能是使用insert允許您指定要添加字符的確切位置的成員函式:
l.insert(l.begin(), c);
所有這些都不是時間有效的。如果您必須在開頭重復添加字符,但不能在結尾添加字符,那么最好只存盤字串的反轉版本并修改演算法的其余部分以使用反轉的字串。追加到字串的末尾通常很便宜,但追加到前面總是很耗時。
uj5u.com熱心網友回復:
l = c l將分配一個新的字串物件,然后覆寫舊的(分配時)。
l = c將就地進行擴展。
假設l有 length N。c當然有長度之一。
c l長度也是如此N 1。所以它使用N 1空間,加上我們已經擁有N的原始空間。l所以2N 1總空間。
l = c就地添加它,所以我們只需要N 1總空間。
(當然,我們不知道具體是如何實作的,但這應該讓您了解記憶體使用情況。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/492603.html
