第25章 字符串与字符数组

第25章 字符串与字符数组

GESP 三级定位
等级知识块考查频率
GESP C++ 三级字符串及其函数(大小写转换、查找、分割、替换)⭐⭐⭐

字符串是表示文本信息的基础。C 语言用字符数组实现字符串;C++ 额外提供 string 类简化操作。三级要求掌握字符数组、cstring 函数与 string 类的常用操作。

25.1 字符数组与字符串基础概念

25.1.1 字符数组

存储字符类型的数组,不一定带结束符 \0,此时它只是字符集合,不是字符串。

char_array.cpp
char arr[3] = {'A', 'B', 'C'}; // 纯字符数组,无 '\0',不是字符串

25.1.2 字符串

以空字符 \0(ASCII 0)作为结束标记的字符序列,必须存放在字符数组中。

c_string.cpp
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "World"; // 编译器自动末尾追加 '\0'

C 字符串的内存布局(含结束符 \0)

25.2 字符数组定义与初始化

25.2.1 逐个字符初始化

init_chars.cpp
char c1[6] = {'C', 'h', 'i', 'n', 'a', '\0'}; // 合法字符串
char c2[3] = {'a', 'b', 'c'}; // 仅字符数组,无结束符

25.2.2 字符串常量初始化

直接用双引号,编译器自动补 \0:

init_literal.cpp
char s1[] = "Hello"; // 长度 6:H e l l o \0
char s2[10] = "Hi"; // 前两位 H i,其余填 '\0'
数组要留 \0 的位置

char s[5] = "Hello"; 会出错:"Hello" 实际占 6 字节(含 \0),但数组只有 5。定义字符数组时长度应 ≥ 字符数 + 1。

25.3 字符串输入输出

25.3.1 C 风格(printf / scanf / puts)

头文件 <stdio.h>

c_io.c
char str[] = "Hello World";
printf("%s\n", str); // %s 读到 '\0' 停止
puts(str); // 自动换行

scanf("%s", str) 遇空格/换行停止(不读空格);gets 已废弃(缓冲区溢出风险),禁用。

25.3.2 C++ cin / cout

cpp_io.cpp
#include <iostream>
using namespace std;
char input[20];
cin >> input; // 空格截断
cout << input << endl;

25.3.3 cin.getline 读取整行(含空格)

getline_char.cpp
char line[50];
cin.getline(line, 50); // 最多读 49 字符,自动补 '\0'

25.4 C 字符串标准库函数(头文件 <cstring>)

25.4.1 strlen 求长度

统计有效字符,不含 \0。

strlen_demo.cpp
char s[] = "Hello";
int len = strlen(s); // len = 5

25.4.2 strcpy 复制

把源串完整复制到目标(含 \0),目标空间必须充足。

strcpy_demo.cpp
char dest[20];
char src[] = "Copy me";
strcpy(dest, src);

25.4.3 strcat 拼接

把源串追加到目标末尾(覆盖原 \0,重补结束符)。

strcat_demo.cpp
char dest[20] = "Hello";
char src[] = " World";
strcat(dest, src); // dest = "Hello World"

25.4.4 strcmp 比较

按 ASCII 逐字符比较:

  • s1 > s2:返回正数;
  • 相等:返回 0;
  • s1 < s2:返回负数。
strcmp_demo.cpp
int r1 = strcmp("apple", "banana"); // 负数('a'<'b')
int r2 = strcmp("hello", "hello"); // 0

25.4.5 strncmp 限定长度比较

仅比较前 n 个字符。

strncmp_demo.cpp
int r = strncmp("abcde", "abcfg", 3); // 前3位相等,返回 0

25.5 C++ string 类(头文件 <string>)

25.5.1 初始化

string_init.cpp
string s1; // 空串
string s2 = "Hello";
string s3(s2); // 拷贝
string s4(5, 'A'); // "AAAAA"

25.5.2 常用成员与运算符

string_api.cpp
string a = "Hi", b = " C++";
string c = a + b; // 拼接: "Hi C++"
a += b; // 追加
int len = a.length(); // 长度
bool e = a.empty(); // 是否空
char ch = a[0]; // 字符访问
bool eq = (a == b); // 直接比较
size_t pos = a.find("C++");// 找不到返回 string::npos

25.5.3 大小写转换(字符串实战)

string 没有内置 toupper,需配合 <cctype> 逐字符转换:

string_toupper.cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string s = "Hello";
for (char &c : s) c = toupper(c); // 转大写 -> "HELLO"
cout << s << endl;
return 0;
}
竞赛建议

string 自带长度管理、自动扩容、越界友好的 at(),比 C 字符数组安全得多。三级及以上竞赛优先用 string,仅在需要极高性能或与 C 接口交互时才用字符数组。

25.6 char 数组与 string 互转

string → C 风格指针:.c_str()

to_cstr.cpp
string s = "Test";
const char* p = s.c_str();

25.7 使用注意事项

  1. C 字符串必须以 \0 结尾,库函数依赖结束符,缺失会乱码;
  2. strcpy、strcat 必须保证目标数组容量足够;
  3. gets 禁用,优先 cin.getline / fgets;
  4. string 更安全,竞赛推荐优先使用;
  5. getline 读取前若有 cin 遗留换行,需清空缓冲区。
缓冲区的溢出风险

char buf[10]; strcpy(buf, "一个很长的字符串..."); 会越界写入,轻则崩溃,重则被利用。用 string 或 strncpy 并手动补 \0 更稳妥。

25.8 选择题练习

1. 关于 C 语言字符串,下列说法正确的是?

A. 字符串就是字符数组,二者完全等价
B. 字符串必须以 \0 作为结束标记
C. char a[3]={'a','b','c'}; 是合法字符串
D. strlen 统计的长度包含 \0

点击展开参考答案与解析

答案:B

解析:字符串是以 \0 结尾的字符数组,二者不等价(A 错);{'a','b','c'} 无 \0 不是字符串(C 错);strlen 不含 \0(D 错),选 B。

2. char s[] = "Hi";,数组 s 的实际长度是?

A. 2
B. 3
C. 1
D. 不确定

点击展开参考答案与解析

答案:B

解析:"Hi" 占 3 字节(H、i、\0),故 s 长度为 3,选 B。

3. 函数 strcmp("abc", "abd") 的返回值?

A. 0
B. 正数
C. 负数
D. 编译错误

点击展开参考答案与解析

答案:C

解析:逐字符比较:'a'==a,'b'==b,第三个 c(99) < d(100),故返回负数,选 C。

4. C++ string 类拼接两个字符串,应使用的运算符是?

A. &
B. +
C. *
D. ,

点击展开参考答案与解析

答案:B

解析:string 重载了 + 用于拼接,选 B。

5. 在 C++ 中把 string 转为 C 风格字符串,应调用?

A. .c_str()
B. .data()(仅某些场景)
C. .to_c()
D. .char_ptr()

点击展开参考答案与解析

答案:A

解析:.c_str() 返回 const char* 形式的 C 风格字符串,选 A。

SShowball OI知识手册