[英]cplusplus.com says that the past-the-end of an std::string “shall not be dereferenced”
如果你能為我清除一些困惑,我想要它。 我正在編寫一個函數來刪除字符串中的重復字符,例如“AB - >”AAABB“。
void remove_dups(std::string& str) {
std::string::iterator it = str.begin();
while (it != str.end()) {
if (*(it+1) == *it) {
str.erase(it+1);
} else {
++it;
}
}
}
它在測試時似乎有效。 但是,我想知道,不應該有柵欄問題嗎? 當'it'是字符串的結尾時,if語句會查看不存在的下一個字符。 根據cplusplus.com,
過去的結尾字符是一個理論字符,它將跟隨字符串中的最后一個字符。 它不應被解除引用。 ( http://www.cplusplus.com/reference/string/string/end/ )
所以我想知道為什么我的函數似乎通過了測試,以及如何以優雅的方式重寫整個事情來繞過fencepost問題。 (對我來說很容易,伙計。我是一個n00b。)
像它一樣重寫它
void remove_dups(std::string& str) {
std::string::iterator it = str.begin();
while (it != str.end()) {
if ((it+1) != str.end() && *(it+1) == *it) {
str.erase(it+1);
} else {
++it;
}
}
}
似乎不優雅。
所以我想知道為什么我的函數似乎通過了測試
未定義的行為並不意味着它不會做你想要的。 它可能完全符合您的要求,只是出於錯誤的原因。 我猜測未定義的行為表現為獲取\\0
字符,它不可能與字符串中的其他字符進行比較。
以及如何以優雅的方式重寫整個事物來繞過fencepost問題
有很多選擇,但我個人的偏好是這樣的
if (it != str.end()) {
++it;
while (it != str.end()) {
/* compare *it and *(it-1) */
}
}
但要注意erase
必須移動元素。 假設您總共刪除了20個字符。 你將字符串的其余部分移回一個字符20次。 如果您稍微修改算法,則不需要這樣做:
void remove_dups(std::string& str) {
std::string::iterator src = str.begin();
std::string::iterator dst = str.begin();
if (src != str.end()) {
++src;
++dst;
while (src != str.end()) {
if (*src != *(src-1)) {
*dst = *src;
++dst;
}
++src;
}
str.resize(dst - str.begin());
}
}
這種方法還有一個好處,即使erase
會在內存中移動字符串,這可能會導致迭代器中斷。
標准允許與最后一個數組元素進行比較,不允許取消引用它。 因此,您可以比較其地址,以驗證循環是否應該結束,但不允許您讀取內容。 由於迭代器主要是指針,因此該規則也適用於字符串,向量,...
另外,請注意string :: erase的非序列版本將迭代器返回到占用已刪除元素的字符,您可以將其用作新的循環迭代器。
我認為最干凈的解決方案是停止在字符串的末尾(字符串的最后一個字符),因為在那時,無論如何都不會執行進一步的操作。
因此, while (it != str.end())
應該是while (it != str.end() && it != (str.end() - 1))
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.