
小字符串优化(Small String Optimization,SBO)是 std::string 实现中一种常见的空间与性能兼顾的技巧:当字符串内容很短时,不堆分配内存,而是直接把字符存进对象内部的固定缓冲区里。
为什么需要 SBO?
频繁构造、拷贝短字符串(比如 "a"、"true"、"id_123")时,每次都调用 new/delete 开销大,还容易造成内存碎片。SBO 把小字符串“装进自己身体里”,避免动态分配,提升缓存局部性,也减少堆操作次数。
SBO 是怎么实现的?
典型实现中,std::string 对象内部包含一个小型固定数组(如 16 或 22 字节),加上长度、容量等字段。它用一个标志位或通过容量值隐式判断当前是否处于“小模式”:
- 字符串长度 ≤ 内置缓冲区大小(减去结尾
\0和元数据开销)→ 数据存在栈上,不 new - 超出阈值 → 切换到堆分配,走常规指针管理流程
- 拷贝/移动时,若源在小模式下,直接 memcpy 内置数组;否则按指针处理
不同标准库的 SBO 阈值不一样
这不是标准强制要求,而是各实现的优化策略:
-
libstdc++(GCC):通常为 15 字节(含结尾
\0),即最多存 14 个字符 - libc++(Clang):常见为 22 字节,可存 21 个字符
- MSVC STL:x64 下一般是 16 字节缓冲,有效载荷约 15 字符
你可以用 sizeof(std::string) 粗略推测——多数实现下是 24 或 32 字节,其中一部分就是留给 SBO 的缓冲空间。
要注意的细节
SBO 带来便利,但也有些易忽略的行为:
- 小字符串的
c_str()仍返回合法地址,但内存不在堆上,不能跨作用域保存指针 -
data()和c_str()在小模式下指向对象内部,生命周期绑定于 string 对象本身 - 移动后原对象可能清空,也可能保留(取决于实现),不要依赖移动后的值
- 自定义分配器可能禁用或改变 SBO 行为(例如某些分配器要求所有内存来自指定池)
基本上就这些。SBO 不是黑魔法,它是 std::string 在常见场景下“悄悄变快”的关键设计之一。理解它,能帮你写出更可控的字符串操作代码,也能看懂一些看似奇怪的性能现象。










