1. 从字符数组到string类的演进之路
在C++编程的早期岁月里,处理文本数据主要依赖于C语言风格的字符数组。这种原始的方式需要开发者手动管理内存、处理边界条件,就像用算盘计算复杂数学题一样繁琐。我记得第一次用字符数组拼接两个字符串时,光是计算长度和分配内存就写了十几行代码,稍有不慎就会引发缓冲区溢出。
C++标准库中的string类彻底改变了这种局面。它封装了底层字符数组的复杂性,提供了长度自动管理、边界检查等现代特性。这就像从手动挡汽车升级到了自动驾驶——你只需要关注要去哪里(业务逻辑),而不必操心如何换挡(内存管理)。
2. 字符数组的原始力量与风险
2.1 基础声明与初始化
字符数组有两种经典初始化方式:
cpp复制// 方式1:指定大小不初始化
char arr1[20]; // 未初始化,可能包含垃圾值
// 方式2:用字符串字面量初始化
char arr2[] = "Hello"; // 自动计算长度(6=5+1)
注意:第二种方式会在末尾自动添加'\0'终止符,所以实际数组长度比可见字符多1。
2.2 内存布局的底层视角
假设我们声明char str[10] = "hello",内存中的实际存储如下:
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6-9 |
|---|---|---|---|---|---|---|---|
| 值 | h | e | l | l | o | \0 | 未初始化 |
这种固定大小的设计导致两个典型问题:
- 空间浪费:如果实际内容远小于数组长度
- 溢出风险:当内容超过预设长度时,会覆盖相邻内存
2.3 常见操作的危险陷阱
cpp复制char str1[10] = "Hello";
char str2[10] = "World";
// 危险操作1:直接赋值
str1 = str2; // 编译错误!数组名是常量指针
// 危险操作2:不安全拼接
strcat(str1, str2); // 可能溢出,str1需要足够空间
我在早期项目中就曾因为strcpy未检查长度导致程序崩溃,后来养成了总是使用strncpy的习惯:
cpp复制strncpy(str1, s
