← 目录 / 第六章 · 数组 / 6.3 字符数组

6.3 字符数组(C 风格字符串)

字符数组是 C 语言处理字符串的方式,本质是 char 类型的一维数组,但有一个特殊规定——末尾必须有结束标记。

字符数组(C 风格字符串)

字符数组是 C 语言处理字符串的方式,本质是 char 类型的一维数组,但有一个特殊规定:末尾必须有 '\0'(空字符,ASCII = 0)作为终止符,告诉程序"字符串到这里结束了"。

6.3.1 结束符 \0 与输入方式

char name[8] = "abcdefg"; // 7个字符 + 1个 \0
字符
a
b
c
d
e
f
g
\0
索引
0
1
2
3
4
5
6
7
🏁
为什么需要 \0?普通数组只要知道首地址和元素个数,就能确定数据范围。但字符数组常常"预留的空间"比"实际用到的字符数"更大(比如上面的例子里定义了 8 个字符的空间,刚好用满)。\0 就是用来标记"有效内容到这里为止"的哨兵,几乎所有处理字符串的函数(包括 cout)都是靠遇到 \0 才知道该在哪里停下来。
C++ · 字符数组 & 输入
1char name[8] = "abcdefg"; // 7 字符 + \0,长度必须 ≥ 8
2char str[] = "Hello"; // 自动推断长度为 6(5 + \0)
3
4// 读取单词(遇空格停止)
5char word[20];
6cin >> word; // 遇空格停止
7
8// 读取含空格的整行
9char sentence[100];
10cin.getline(sentence, 100); // 读取整行,包括空格
11
12// 混用 cin 和 getline 时需先清空缓冲区
13int a; cin >> a;
14cin.sync(); // 清空缓冲区残留的换行符
15cin.getline(sentence, 100);
📌
getline 注意事项:长度参数不能超过数组长度;只能读取字符类型;默认以换行符作为结束符。使用 cin 读取整数后调用 cin.sync() 清空缓冲区,否则 getline 会直接读到残留的换行符。

6.3.2 常用转义字符

字符串里有些符号没办法直接打出来(比如换行、Tab键),还有些符号本身就是字符串语法的一部分(比如引号),直接写会和字符串的边界搞混。C++ 用反斜杠 \ 开头的组合来表示这些特殊字符,称为转义字符——"转义"的意思是让紧跟在 \ 后面的字符不再表示它本来的含义,而是代表一个特殊符号。

转义字符含义ASCII使用示例
排版控制
\n换行10cout << "Hello\nWorld";
\t水平制表符(Tab)9cout << "A\tB\tC";
符号本身
\\一个反斜杠 \92cout << "C:\\Users";
\"一个双引号 "34cout << "\"Hello\"";
\'一个单引号 '39char c = '\'';
字符串结束
\0字符串终止符(空字符)0char s[] = {'H','i','\0'};
📖
为什么 \" 和 \\ 需要转义?双引号 " 本身是字符串的边界符号,如果字符串内容里直接出现一个 ",编译器会误以为字符串在这里就结束了。反斜杠 \ 同理——它是转义序列的开头,直接写一个 \ 会被当成某个转义字符的开始,必须写两个 \\ 才能表示"就是一个普通反斜杠"。

6.3.3 常用字符串函数(#include <cstring>

字符数组本质上只是普通数组,并不会自带"求长度""拼接""比较"这些能力——毕竟编译器只把它当成一串字符,不知道你把它当"字符串"在用。<cstring> 头文件提供了一整套配套函数,专门用来完成这些字符串操作,使用前记得先 #include <cstring>

函数功能示例
查询类
strlen(s)返回字符串有效长度(不含 \0)strlen("Hello") → 5
strcmp(s1, s2)比较字符串大小(按 ASCII),相等返回 0strcmp("abc","abc") → 0
修改类
strcpy(dest, src)将 src 复制到 deststrcpy(b, a)
strcat(s1, s2)将 s2 拼接到 s1 末尾strcat(s, " World")
strlwr(s)将字符串转为全小写strlwr("HELLO") → "hello"
strupr(s)将字符串转为全大写strupr("hello") → "HELLO"
转换类
atoi(s)字符串转整数(非数字返回 0)atoi("123") → 123
atof(s)字符串转 double(非数字返回 0.0)atof("3.14") → 3.14
⚠️
这些函数都不检查目标空间够不够:strcpystrcat 在写入时完全不会检查目标数组是否装得下,多出来的内容会直接溢出到相邻内存(详见 6.3.4 节的陷阱说明)。使用前先确认目标数组留够了空间。
🔤
字符分类与大小写转换:判断一个字符是不是字母/数字,或者做大小写转换,用 <cctype> 提供的 isalphaisdigittouppertolower 等函数即可,已在 2.2 节详细介绍过,这里不再重复。

6.3.4 常见陷阱与易错点

把本节内容汇总成几条最容易踩坑的规则:

1
忘记为 \0 预留空间:定义字符数组时,大小必须至少是"字符个数 + 1",多出来的这一个位置留给 \0char s[5] = "hello" 会因为 5 个字符正好占满数组、没有位置放 \0 而出错,正确写法是 char s[6] 或直接省略大小让编译器自动推断。
2
strcpy 目标数组太小导致溢出:strcpy 不会检查目标数组够不够大,如果 dest 的空间比 src 小,多出来的内容会溢出到相邻内存,是非常危险的隐藏 bug。使用前一定确认目标数组的大小足够容纳源字符串(含 \0)。
3
用 == 比较字符数组内容:字符数组本质是数组,s1 == s2 比较的是两个数组的地址,而不是字符串的内容,即使两个数组内容完全一样,结果通常也是 false。判断内容是否相同必须用 strcmp(s1, s2) == 0
4
手动逐字符构造字符串却忘了补 \0:如果不是用字符串字面量赋值,而是像 s[0]='H'; s[1]='i'; 这样一个个字符手动填充,最后必须手动加上 s[2]='\0';,否则数组内容"看起来对",但用 strlencout 等函数处理时会因为找不到结束标记而读到后面无关的垃圾数据。