8 string字符串及C风格应用(二)
正在加载视频播放器…
GESP C++ 三级:string 字符串及 C 风格应用梳理
核心目标:在掌握
string类的基础上,掌握 C 风格字符串(char数组)的定义、输入输出、核心函数及与string的互转,理解'\0'结束符的作用,能根据题目要求选择合适的方式处理字符串。
string安全便捷,自动管理内存;C 风格字符串固定长度,以'\0'结尾,函数操作需自行保证空间充足。strlen算有效字符数,sizeof算数组总字节数,两者千万不能混。
一、string 与 C 风格字符串的核心区别
这是三级考试的高频选择题考点。C++ 中同时存在两种字符串表示方式,各有适用场景。
| 对比项 | string(C++ 风格) |
char 数组(C 风格) |
|---|---|---|
| 头文件 | <string> |
<cstring> |
| 定义方式 | string s = "Hello"; |
char s[] = "Hello"; |
| 内存管理 | 自动管理,动态扩展 | 手动管理,固定大小 |
| 长度获取 | s.size() / s.length() |
strlen(s) |
| 末尾标志 | 不需要手动添加 | 必须有 '\0' |
| 赋值 | s = "world"; |
需用 strcpy() |
| 拼接 | s += "world"; |
需用 strcat() |
| 比较 | s1 == s2 |
需用 strcmp() |
| 越界检查 | s.at(i) 会抛异常 |
无检查,越界行为未定义 |
| 推荐度 | GESP 考试推荐使用 | 需掌握,使用需谨慎 |
最关键的差异:C 风格字符串以 '\0' 作为结束标志。字符串 "GESP" 在内存中实际占用 5 个字节:'G'、'E'、'S'、'P'、'\0',有效下标为 0~3,'\0' 位于下标 4。
二、C 风格字符串的定义与初始化
1. 必须包含头文件
#include <cstring> // 使用 strcpy、strlen 等 C 风格函数
//万能头包含
2. 三种初始化方式
// 方式一:字符串字面量初始化,编译器自动添加 '\0'
char s1[] = "GESP"; // 数组长度自动为 5(4个字符 + '\0')
// 方式二:显式指定长度,剩余位置补 '\0'
char s2[10] = "GESP"; // s2 = "GESP\0\0\0\0\0\0"
// 方式三:逐字符初始化(必须手动加 '\0',否则不是字符串!)
char s3[5] = {'G', 'E', 'S', 'P', '\0'}; // 正确
char s4[4] = {'G', 'E', 'S', 'P'}; // 危险!缺少 '\0'
致命陷阱:char str[5] = {'H','e','l','l','o'}; 缺少 '\0' 终止符,使用 cout << str 或 strlen(str) 会越界访问内存,输出结果不可预测。
三、C 风格字符串的输入输出
1. 输入
char s[100];
cin >> s; // 遇到空格/换行停止,不读空格
cin.getline(s, 100); // 读入整行(含空格),最多 99 个字符 + '\0'
cin.getline(数组名, 长度):从输入流读取一行,直到遇到换行符或读满 长度-1 个字符,自动在末尾添加 '\0'。换行符被读取并丢弃。
混合使用 cin >> 和 cin.getline 时的陷阱:
int n;
char s[100];
cin >> n; // 输入数字后按回车
cin.getline(s, 100); // ❌ 会立即读到空行(缓冲区残留的换行符)
解决方法:在两者之间加 cin.ignore() 清空缓冲区。
2. 输出
char s[] = "GESP";
cout << s << endl; // 输出 GESP(自动在 '\0' 处停止)
四、C 风格字符串核心函数
1. 长度计算:strlen
char s[] = "GESP";
cout << strlen(s) << endl; // 4(不含 '\0')
cout << sizeof(s) << endl; // 5(含 '\0',数组总字节数)
strlen 与 sizeof 的本质区别:
| 函数 | 计算内容 | 示例(char s[] = "GESP") |
|---|---|---|
strlen(s) |
实际字符数,不含 '\0' |
4 |
sizeof(s) |
数组总字节数,含 '\0' |
5 |
必记:
strlen是函数,运行时计算;sizeof是运算符,编译时确定。考试中经常考两者的输出差异。
2. 复制:strcpy / strncpy
char dest[20];
char src[] = "GESP";
strcpy(dest, src); // 将 src 复制到 dest(dest 空间必须足够大)
strncpy(dest, src, 2); // 只复制前 2 个字符,结果 "GE"(不自动加 '\0')
安全守则:dest 的空间必须 ≥ strlen(src) + 1,否则会缓冲区溢出。
3. 拼接:strcat
char s1[20] = "GESP";
char s2[] = " C++";
strcat(s1, s2); // 将 s2 拼接到 s1 末尾,结果 "GESP C++"
安全守则:s1 的剩余空间必须 ≥ strlen(s2) + 1。
4. 比较:strcmp / strncmp
strcmp("Apple", "Banana"); // 返回负值('A' < 'B')
strcmp("apple", "Apple"); // 返回正值('a' > 'A')
strcmp("GESP", "GESP"); // 返回 0(相等)
strncmp("GESP", "GET", 2); // 只比较前 2 个字符,返回 0("GE" == "GE")
返回值规则:按字典序逐字符比较 ASCII 码,首对不同字符的 ASCII 码差值即为返回值。完全相同时返回 0。
注意:
strcmp返回的是差值,不是固定的 -1/0/1。strcmp("a", "A")返回 32(97-65),不是 1。
5. 查找字符:strchr
char s[] = "GESP";
char *p = strchr(s, 'E'); // 返回指向 'E' 的指针,未找到返回 NULL
if (p != NULL) {
cout << *p << endl; // E
}
6. 初始化:memset
char s[100];
memset(s, 0, sizeof(s)); // 将数组所有字节置为 0(即全部为 '\0')
常用于清空字符数组,等价于 char s[100] = {0};。
五、string 与 C 风格字符串的互转
1. string → C 风格字符串:c_str()
当 C 风格函数需要 char* 参数时,可以用 c_str() 将 string 转换:
string s = "GESP";
int len = strlen(s.c_str()); // 将 string 转为 const char* 后调用 strlen
cout << len << endl; // 4
注意:
c_str()返回const char*,不能修改内容。data()与c_str()类似,但c_str()保证末尾有'\0',推荐使用c_str()。
2. C 风格字符串 → string
char s[] = "GESP";
string str = s; // 直接赋值即可,自动转换
string str2(s); // 或调用构造函数
六、真题精讲
真题1:分割字符串
题目描述:从左到右扫描字符串,第一次遇到 1 个空格时分割,对右侧继续扫描,第一次遇到连续 2 个空格时分割,下一次需要连续 3 个空格,以此类推。输出每次分割左侧的字符串,以及最后一次分割的右侧字符串。
解题思路:使用 string 的 find 和 substr 实现。
#include<bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s);
int need = 1;
int start = 0;
bool split = false;
while (true) {
string target(need, ' ');
size_t pos = s.find(target, start);
if (pos == string::npos) {
if (!split) cout << s << endl;
else cout << s.substr(start) << endl;
break;
}
cout << s.substr(start, pos - start) << endl;
split = true;
start = pos + need;
need++;
}
return 0;
}
关键细节:
getline(cin, s)读入含空格的整行,不能用cin >> s。s.find(target, start)从start开始查找目标子串。- 空字符串分割时必须输出空行,不能省略(样例 2 特别强调)。
split标志区分“从未分割”和“已分割”的输出逻辑。
真题2:字符数组长度辨析(选择题)
定义字符数组
char str[20] = {'G', 'E', 'S', 'P'};,则str的字符串长度为( )。
答案:4。str初始化时只显式赋了 4 个字符,其余 16 个位置自动补'\0',因此strlen(str)返回 4。
真题3:sizeof 与 strlen 辨析(选择题)
char msg[] = "GESP"; cout << strlen(msg) << " " << sizeof(msg);输出什么?
答案:4 5。strlen返回有效字符数 4,sizeof返回数组总字节数 5(含'\0')。
七、易错点汇总
1. '\0' 终止符缺失(最高频陷阱)
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ 缺少 '\0'
cout << strlen(str); // 结果不可预测
诊断公式:数组长度 ≥ 可见字符数 + 1。当字符数组用于存储字符串时,必须预留一个位置给 '\0'。
2. strlen vs sizeof 混淆
char s[] = "GESP";
cout << strlen(s) << endl; // 4(有效字符数)
cout << sizeof(s) << endl; // 5(数组总字节数)
3. strcmp 返回值理解错误
if (strcmp(s1, s2) == 1) // ❌ 错误:返回值不一定是 1
if (strcmp(s1, s2) > 0) // ✅ 正确:只判断正负
if (strcmp(s1, s2) == 0) // ✅ 正确:相等判断
4. cin.getline 与 cin >> 混用
int n;
char s[100];
cin >> n;
cin.getline(s, 100); // ❌ 读到空行
// 正确:cin.ignore(); cin.getline(s, 100);
5. strcpy 目标空间不足
char dest[3];
strcpy(dest, "GESP"); // ❌ 缓冲区溢出,dest 需要至少 5 字节
6. substr 起始位置越界
string s = "GESP";
s.substr(4, 1); // ❌ 抛出 out_of_range 异常(起始位置必须 < s.length())
s.substr(2, 100); // ✅ 不会报错,截取到末尾
7. 字符数组长度必须是常量
int n = 10;
char s[n]; // ❌ 标准 C++ 不允许变长数组
char s[10]; // ✅ 正确
八、速查表
string 常用函数
| 需求 | 写法 |
|---|---|
| 长度 | s.size() / s.length() |
| 判空 | s.empty() |
| 访问字符 | s[i] / s.at(i) |
| 查找 | s.find(sub, pos),找不到返回 string::npos |
| 截取 | s.substr(pos, len) |
| 插入 | s.insert(pos, str) |
| 删除 | s.erase(pos, len) |
| 替换 | s.replace(pos, len, str) |
| 追加 | s.append(str) / s += str |
| 末尾加字符 | s.push_back(c) |
| 转 C 风格 | s.c_str() |
C 风格字符串函数
| 需求 | 写法 | 关键注意 |
|---|---|---|
| 长度 | strlen(s) |
不含 '\0' |
| 数组字节数 | sizeof(s) |
含 '\0' |
| 复制 | strcpy(dest, src) |
dest 空间要足够 |
| 限长复制 | strncpy(dest, src, n) |
不自动加 '\0' |
| 拼接 | strcat(dest, src) |
dest 剩余空间要足够 |
| 比较 | strcmp(s1, s2) |
返回差值,非固定 ±1 |
| 限长比较 | strncmp(s1, s2, n) |
只比较前 n 个字符 |
| 查找字符 | strchr(s, c) |
未找到返回 NULL |
| 初始化 | memset(s, 0, sizeof(s)) |
全部置 '\0' |
| 输入整行 | cin.getline(s, len) |
含空格 |
| 转 string | string str = s; |
直接赋值 |
九、总结
string 与 C 风格字符串是 GESP 三级字符串模块的两大核心。
string自动管理内存,C 风格字符串固定长度且必须以'\0'结尾。cin >> s遇空格停止,读整行用getline(cin, s)(string)或cin.getline(s, len)(char 数组)。strlen返回有效字符数,sizeof返回数组总字节数,两者经常一起考。strcpy复制、strcat拼接、strcmp比较,目标空间必须足够大。strcmp返回的是 ASCII 差值,不要用== 1判断。cin >>和cin.getline混用时,中间要加cin.ignore()。- C 风格字符串的三种函数操作(复制、拼接、比较)在
string中分别对应=、+=、==。 string转 C 风格用s.c_str(),C 风格转string直接赋值。- 字符数组长度必须是编译期常量,不能用变量。
- 所有 C 风格字符串操作前,先确认
'\0'存在且目标空间充足。
复习建议:重点练习 strlen 与 sizeof 的辨析、strcmp 返回值判断、cin.getline 与 cin >> 混用、string 与 char 数组的互转四类选择题,以及“分割字符串”类编程题。考试中先判断题目要求使用哪种字符串类型,再选择对应的函数,最后检查 '\0' 和空间是否充足。