std::string 入门
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 5 篇。C 语言的字符串是字符数组,处处要小心;std::string把这一切封装起来:自动管理内存、直接+拼接、size()而非strlen。本篇讲string的构造、拼接、查找、子串,以及和 C 字符串互相转换。
本文要回答的问题
std::string相比 C 的char[]到底爽在哪?- 拼接、查找、子串这些常用操作怎么写?
- 怎么和 C 的
const char*互相转换?
一、为什么用 std::string
C 的字符串是"字符数组 + 结尾 '\0'"的约定,所有操作都是手动:
// C 风格
char buf[64];
strcpy(buf, "hello");
strcat(buf, " world"); // 缓冲区溢出风险
if (strcmp(buf, "hello") == 0) { ... }C++ 的 std::string 是一个类,内存自动管理,操作符直接支持:
#include <string>
std::string s = "hello";
s += " world"; // 直接拼接
if (s == "hello") { ... } // 直接比较| 操作 | C(char[]) | C++(std::string) |
|---|---|---|
| 拼接 | strcat 手动,注意缓冲区 | + / +=,自动扩容 |
| 比较 | strcmp 返回 0 | == / < 返回 bool |
| 长度 | strlen O(n) | size() O(1)(内部记录) |
| 拷贝 | strcpy 危险 | 拷贝构造自动 |
| 内存 | 手动数组,易越界 | 自动管理 |
核心差别:C 的字符串是"数据 + 约定",C++ 的 string 是"数据 + 方法 + 自动内存"。
二、构造与基本操作
#include <iostream>
#include <string>
int main() {
std::string a; // 空串
std::string b = "hello"; // 从字面量构造
std::string c(b); // 拷贝构造
std::string d(b, 1, 3); // 子串:"ell"(从位置1取3个字符)
std::string e(5, 'x'); // 5 个 x:"xxxxx"
std::cout << b.size() << std::endl; // 5(不含 '\0')
std::cout << b.length() << std::endl; // 5,和 size 一样
std::cout << b[0] << std::endl; // 'h'(可下标访问)
std::cout << b.empty() << std::endl; // 0(false),非空
return 0;
}注意:size() 和 length() 完全等价,size() 更符合 STL 习惯。s[0] 下标访问和数组一样(越界是未定义行为,谨慎),或用 s.at(0) 带检查(越界抛异常)。
三、拼接与查找
拼接:
std::string first = "张三";
std::string last = "李四";
std::string full = first + "、" + last; // "张三、李四"
full += "!"; // "张三、李四!"+ 要求至少一边是 std::string(字面量和 string 混用也 OK,C++ 会转换)。
查找:
std::string s = "hello world";
size_t pos = s.find("world"); // pos == 6
if (pos == std::string::npos) {
// 没找到;npos 是"不存在"的哨兵值
}
size_t pos2 = s.find('o'); // 找字符,返回 4find 找不到返回 std::string::npos(一个巨大的 size_t 值)。判断查找失败必须 == npos,不能写成 > 0 或 == -1(新手高频错误)。
子串:
std::string s = "hello world";
std::string sub = s.substr(6, 5); // 从位置6取5个:"world"
std::string head = s.substr(0, 5); // "hello"四、和 C 字符串互相转换
C++ 代码常要调用 C 风格 API(如 fopen、printf 相关、系统调用),需要转换:
std::string s = "data.txt";
// string → const char*(只读):c_str()
const char* cs = s.c_str(); // 指向内部缓冲,末尾有 '\0'
// string → char*(可写):
char buf[128];
strcpy(buf, s.c_str()); // 拷贝到自己的数组
// const char* → string:直接构造
const char* legacy = "old style";
std::string modern(legacy);警告:c_str() 返回的指针指向 string 内部缓冲,string 一旦被修改或销毁,该指针就失效。不要长期保存它——要用时再取,用完即弃。
五、string 的常见坑
npos判断:if (s.find(x) != std::string::npos)是标准写法;- 下标越界:
s[100]不报错(UB),短字符串访问越界是调试噩梦,不确定时用s.at(100)(越界抛异常); c_str()悬垂:持有c_str()指针期间别改 string;- 拼接性能:循环里
s += c频繁扩容有开销,先s.reserve(n)预留容量(高手层string 深入详解); std::string与char[]:传参默认按值拷贝(有开销),只读传const std::string&,见值传递 vs 引用传递。
六、与本站主线衔接
- string 内部是"堆上连续字符 + size/capacity",内存布局见 L3 内存子系统;
- string 的扩容策略(翻倍 vs 线性)影响缓存与性能,见高手层STL 容器选型深挖;
- C 字符串的坑与
strlen/strcpy陷阱,见 C 语言字符串陷阱。
一句话总结
std::string 把 C 字符串的"数据 + 约定"升级为"数据 + 方法 + 自动内存"——+ 拼接、== 比较、size() O(1)、find/substr 随手用,和 C 字符串用 c_str() / 构造函数互转;记住三个坑:npos 判断、越界用 at、c_str() 不长期持有。
上一篇:const 与 constexpr 下一篇:运算符与类型转换