c++oding="utf-8" ?>
用[]操作符修改单个字符最简单,返回char&支持赋值;at()从c++11起也返回char&可赋值,但需注意越界检查:[]不检查、at()抛异常;replace()适合批量替换且可能优化为就地覆盖。

直接用 [] 操作符修改单个字符最简单
只要 string 不是 const,就能像访问数组一样用下标改值,底层是可写的连续内存。
常见错误是误以为 string::at() 也能赋值——它确实能读,但返回的是 char(非引用),不能用于修改;而 [] 返回的是 char&,支持赋值。
- 确保索引在合法范围内:
s[i]不检查越界,越界行为未定义;s.at(i)会抛std::out_of_range异常,适合调试时用 - 空字符串或索引为负数时,
[]仍不报错,但结果不可预测 - 修改操作不会改变字符串长度,也不会触发重新分配(除非你碰巧在写入时触发了 SSO 到堆的切换,但那是实现细节,不影响使用)
std::string s = "hello"; s[0] = 'H'; // ✅ 合法,s 变成 "Hello" s.at(1) = 'E'; // ❌ 编译失败:cannot assign to return value because 'char' is not a modifiable lvalue s.at(1) = 'E'; // ✅ 正确写法:at() 返回 char&(C++11 起),可赋值(注意:早期标准有差异,但主流编译器现在都支持)
用 replace() 或 assign() 替换子串更灵活
如果要改不止一个字符,或者想按位置+长度批量替换,replace() 比循环赋值更清晰、更高效。
它本质是先删后插,但对小范围替换(尤其在 SSO 容量内)现代 libstdc++/libc++ 会优化为就地覆盖。
-
s.replace(pos, len, str):从pos开始删len个字符,插入str - 想只改一个字符但需要边界检查?用
s.replace(i, 1, 1, 'X'),比手写if (i 更啰嗦,但统一了接口 - 注意
len超出剩余长度时,replace会自动截断,不会崩溃
std::string s = "world"; s.replace(0, 2, "HE"); // ✅ 变成 "HErld" s.replace(3, 10, "XYZ"); // ✅ 自动按实际可用长度处理,变成 "HErXYZ"
迭代器修改适用于算法场景
当你已经拿到某个字符的迭代器(比如用 find() 找到某字符位置),直接解引用赋值是最自然的方式。
这和 [] 效果一致,但更契合 STL 算法风格,也避免重复计算索引。
-
auto it = std::find(s.begin(), s.end(), 'l');→if (it != s.end()) *it = 'L'; - 用
begin() + i模拟索引?可以,但不如直接用[]直观,且无额外收益 - 注意:
string迭代器失效规则和vector一致——只有容量变化(如push_back触发重分配)才会让所有迭代器失效
别踩这些坑
看似简单的字符修改,实际容易掉进隐式转换、编码和生命周期陷阱里。
- 用
char字面量没问题,但别传int(比如s[i] = 65)——可读性差,且在有符号/无符号char平台上行为可能不同 - UTF-8 字符串不能靠索引改“字符”:一个 Unicode 字符可能占多个
char,s[i]改的只是某个字节,大概率破坏编码 - 把
string的c_str()指针拿来改内容?不行。c_str()返回const char*,且标准不保证其内存可写;即使强制转char*,也是未定义行为
真正要改 UTF-8 字符,得先用库(如 ICU、utf8cpp)定位码点边界,再整体替换子串——索引在这里完全失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











