一、先弄清楚:C++ 里有哪些'字符串'?
在 C++ 中,和'字符串'相关的常见类型主要有:
- C 风格字符串(C-style string)
- 本质:以
'\\0'结尾的char数组 - 例:
char s[] = "hello";
- 本质:以
std::string(C++ 标准库字符串)- 本质:封装好的动态字符串类,自动管理内存
- 例:
std::string s = "hello";
- 宽字符/Unicode 相关的:
- 、、、、、 等
本文介绍了 C++ 中字符串类型的区别,重点讲解了 std::string 的多种初始化方式(默认、字面量、重复字符等)及基本用法(访问、长度、拼接、查找等)。通过具体代码示例分析了二维 string 数组的初始化与遍历逻辑,对比了 C 风格字符串的差异,并给出了优先使用 std::string 的建议。
在 C++ 中,和'字符串'相关的常见类型主要有:
'\\0' 结尾的 char 数组char s[] = "hello";std::string(C++ 标准库字符串)
std::string s = "hello";wchar_tchar16_tchar32_tstd::wstringstd::u16stringstd::u32string日常开发中,绝大部分情况都用 std::string,只有与底层 API、C 接口打交道时,才不得不使用 C 风格字符串。
你看到的代码片段:
string groups[6][6]{};for(auto& s : allowed){ groups[s[0]-'A'][s[1]-'A']+= s[2];}
这里用的是 std::string 的二维数组:std::string groups[6][6]。在 C++ 中,string 是 std::string 的别名(using std::string; 之后)。
std::string 的常见初始化方式std::string s1;// 默认初始化,内容为空字符串 "" std::string s2{};// 值初始化,效果同上,也是 ""
你看到的:
string groups[6][6]{};
这行的含义是:
std::string 数组{} 表示值初始化,把每个元素都初始化为'空字符串'等价于:
string groups[6][6];// 在这种情况下,同样会默认构造出 36 个空字符串
加不加 {} 对 std::string 来说差别不大(都是默认构造为 ''),但 {} 是一种更明确的'值初始化'写法。
std::string s1 ="hello"; std::string s2("world"); std::string s3{"hi"};// 列表初始化
这些写法都可以,把右边的 C 字符串字面量拷贝到 std::string 里。
std::string s4(5,'a');// "aaaaa"
std::string src ="abcdefg"; std::string s5 = src;// 拷贝整个字符串 std::string s6(src,2);// 从下标 2 开始到末尾:"cdefg" std::string s7(src,2,3);// 从下标 2 开始取 3 个字符:"cde"
std::string src ="abcdef"; std::string s8(src.begin()+1, src.begin()+4);// "bcd"
std::string 的基本用法只要理解:std::string 就像一个会自动扩容的字符数组 + 常用操作方法,用起来就很自然。
std::string s ="hello";// 下标操作char c1 = s[0];// 'h',不做越界检查 s[1]='A';// s 变成 "hAllo"// at() 会做越界检查,越界会抛出异常char c2 = s.at(1);
在你看到的代码里:
groups[s[0]-'A'][s[1]-'A']+= s[2];
这里也在用下标访问 std::string 的字符:
s[0]:第一个字符s[1]:第二个字符s[2]:第三个字符再解释一下这个表达式:
s[0]-'A'
假设 s[0] 是一个大写字母 'A' ~ 'F'(因为数组大小是 6),例如:
'A' - 'A' == 0'B' - 'A' == 1'C' - 'A' == 2所以 s[0] - 'A' 被当作 行索引,s[1] - 'A' 当作 列索引。
而 s[2] 是一个字符,被 追加 到 groups[i][j] 这个字符串里(下面会讲'追加'的用法)。
std::string s ="hello"; size_t len1 = s.size(); size_t len2 = s.length();// 和 size() 等价
+ 和 +=std::string s1 ="hello"; std::string s2 ="world"; std::string s3 = s1 + s2;// "helloworld" std::string s4 = s1 +" ";// "hello " s1 +=" world";// s1 变成 "hello world" s2 +=!;// s2 变成 "world!"
现在再看你那行代码:
groups[s[0]-'A'][s[1]-'A']+= s[2];
等价含义是:
// 取出这个单元格里的字符串 std::string& cell = groups[s[0]-'A'][s[1]-'A'];// 把 s[2] 这个字符追加到 cell 的末尾 cell.push_back(s[2]);// 等价于 cell += s[2];
groups 是一个 6×6 的字符串数组,初始全是空串 ""。
当遍历 allowed 里的每一个 s 时,把 s[2] 这个字符分类放到 groups[s[0]-'A'][s[1]-'A'] 里。
一个类比:
groups 看作 6×6 的'格子',每个格子里是一个字符串篮子s = "ABC":
'C' 扔进 ( 'A'-'A' , 'B'-'A' ) = (0, 1) 这个格子的篮子里最终,每个格子会存放一个由多个字符拼起来的字符串。
std::string s ="hello";// 插入 s.insert(1,"ABC");// "hABCello"// 擦除 s.erase(1,3);// 从下标 1 起删 3 个,变回 "hello"// 替换 s.replace(1,2,"XX");// "hXXlo"
std::string s ="hello world";auto pos = s.find("lo");// 返回 3if(pos != std::string::npos){// 找到了// ...}
auto pos2 = s.rfind('o');// 从右往左找,返回 7
std::string s ="abcdefgh"; std::string sub = s.substr(2,3);// 从下标 2 开始,取 3 个字符:"cde" std::string sub2 = s.substr(2);// 从 2 到末尾:"cdefgh"
std::string s ="hello";constchar* p = s.c_str();// 或 s.data(),得到以 '\\0' 结尾的指针,只读// 从 const char* 创建/复制constchar* c ="world"; std::string s2(c);// "world"
std::string 的数组、容器、范围 for你看到的是 std::string 的 二维数组 + 范围 for:
string groups[6][6]{};for(auto& s : allowed){ groups[s[0]-'A'][s[1]-'A']+= s[2];}
逐句拆一下:
string groups[6][6]{};
string 数组""for (auto& s : allowed)
allowed(应该是如 vector<string> 或 array<string, N>)auto& s 表示:s 是每个元素的引用,并且类型由编译器根据 allowed 的元素类型推导,一般就是 std::string&groups[s[0]-'A'][s[1]-'A'] += s[2];
s 这个字符串的第 3 个字符 s[2] 追加到一个格子中s[0] - 'A's[1] - 'A'举个具体例子帮你完全理解:
std::vector<std::string> allowed ={"ABC","ABD","BAC","BAE"}; std::string groups[6][6]{};for(auto& s : allowed){ groups[s[0]-'A'][s[1]-'A']+= s[2];}
循环执行过程:
s = "ABC"
s[0] = 'A' → 行索引 0s[1] = 'B' → 列索引 1s[2] = 'C'groups[0][1] += 'C';groups[0][1] 是 "",现在变成 "C"s = "ABD"
[0][1]groups[0][1] += 'D'; "C" + 'D' → "CD"s = "BAC"
s[0] = 'B' → 行索引 1s[1] = 'A' → 列索引 0s[2] = 'C'groups[1][0] += 'C';"" 变成 "C"s = "BAE"
[1][0]groups[1][0] += 'E'; "C" + 'E' → "CE"最后:
groups[0][1] == "CD"groups[1][0] == "CE"""这类写法在'预处理 + 构建查找表 / 分类表'时很常见。
你说'感觉不是很懂字符串了',本质上是:C 风格字符串 和 std::string 在语法和行为上差异比较大,混起来看容易晕。
char s1[]="hello";// 数组,大小为 6,包括末尾 '\\0'char* s2 ="world";// 指向字符串常量(在现代 C++ 中不推荐修改)
它只是一个 char 数组,靠末尾的 '\\0' 来表示结束。
<cstring>)#include<cstring>char a[20]="hello";char b[20];// 拷贝 std::strcpy(b, a);// b = "hello"// 追加 std::strcat(a," world");// a = "hello world"// 比较int cmp = std::strcmp(a, b);// 按字典序比较
相比之下,std::string:
'\\0'std::string,不要再自己手动分配 char* + new[] 来拼字符串。std::string::c_str() 或 data() 拿到 const char*不要混淆:
std::string s ="abc";// C++ 字符串char cstr[]="abc";// C 风格字符串
初始化数组时:
std::string arr[10];// 每个都是 "" std::string arr2[10]{};// 同样每个都是 ""
对 std::string 来说,两者一样;但 {} 的风格在现代 C++ 中更被推荐。
string groups[6][6]{}std::string 的二维数组,每个元素初始化为空字符串。for (auto& s : allowed)allowed,s 是每一个 std::string 的引用。groups[s[0] - 'A'][s[1] - 'A'] += s[2];
s[0]、s[1] 这两个大写字母映射成 0~5 的整数索引groups 的行、列坐标s[2] 这个字符追加到已有的字符串后面这整段代码逻辑就是:
根据字符串 s 的前两个字符对第三个字符进行'二维分类',并在每个分类格子里把同类的字符串在一起。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online