对string的begin()迭代器使用std::prev有多不安全?附去重代码优化咨询
代码存在的问题
- 存在明确的未定义行为:你写的循环首次迭代时
it就是str.cbegin(),C标准明确规定,对前向迭代器的起始位置调用std::prev属于未定义行为,和C语言中访问数组首地址之前的内存风险完全一致,不存在“C规则更宽松”的说法。你当前代码能正常运行只是编译器未开启迭代器边界检查的巧合,换编译环境、开优化都可能直接崩溃。 - 逻辑本身存在缺陷:就算忽略未定义行为,你也没有把字符串的第一个字符加入结果集,比如输入
"ab"的运行结果会是"b",直接丢失了首字符,完全不符合需求。
更优的实现方案
两种可选方案均为O(n)时间复杂度,正确性有保障:
方案1:直接使用STL内置的std::unique
这是最简洁、经过标准库充分优化的实现,专门用于去除相邻重复元素:
#include <algorithm> #include <string> std::string suppDouble(const std::string& str) { if (str.size() <= 1) return str; std::string res = str; auto dup_end = std::unique(res.begin(), res.end()); res.erase(dup_end, res.end()); return res; }
如果不需要保留原字符串,你还可以直接传值而非传常量引用,省去一次拷贝开销。
方案2:手写实现(适合需要自定义去重规则的场景)
提前预分配内存避免动态扩容,同时规避迭代器边界问题:
#include <string> std::string suppDouble(const std::string& str) { if (str.empty()) return {}; std::string token; token.reserve(str.size()); // 提前分配足够空间,消除后续push_back的扩容开销 token.push_back(str.front()); for (auto it = str.cbegin() + 1; it != str.cend(); ++it) { if (*it != token.back()) { token.push_back(*it); } } return token; }
内容的提问来源于stack exchange,提问作者Etienne Armangau
相关产品推荐
相关产品推荐

