5. 数组与字符串:一维数组、二维数组、字符数组与C风格字符串、string类、数组与字符串的遍历

数组和字符串,说白了就是C++里最基础的数据容器。我刚开始学的时候,觉得这东西太简单了,不就是一堆数据排排坐吗?结果后来在项目里踩了不少坑,才发现这里面的门道还真不少。今天咱们就好好聊聊这个话题。

5.1 一维数组:最简单的数据集合

一维数组就像一排带编号的抽屉。每个抽屉里放一个数据,编号从0开始。嗯,这里要注意,C++的数组索引是从0开始的,不是从1。我见过不少新手在这上面栽跟头。

// 声明一个能放5个整数的数组
int scores[5];

// 声明并初始化
int scores[5] = {85, 92, 78, 90, 88};

// 也可以省略大小,编译器会自动计算
int scores[] = {85, 92, 78, 90, 88};

小提示:我个人习惯在声明数组时就初始化,哪怕只是全部赋0。因为未初始化的局部数组里存的是随机值,这在调试时特别容易让人抓狂。

访问数组元素很简单,用方括号加索引就行:

cout << "第三个成绩是:" << scores[2] << endl;  // 输出78
scores[0] = 95;  // 修改第一个元素

警告:C++不会检查数组下标是否越界!我曾经在项目里遇到过,一个同事写了 scores[5] = 100,数组只有5个元素,索引最大是4。结果程序运行时好时坏,查了两天才发现是越界写入了相邻内存。这种bug特别隐蔽,一定要小心。

5.2 二维数组:表格数据的好帮手

二维数组,你可以把它想象成一个表格,有行有列。比如存储一个班级3个学生、每科4门课的成绩:

// 3行4列的二维数组
int grades[3][4] = {
    {85, 92, 78, 90},  // 第一个学生
    {88, 76, 95, 82},  // 第二个学生
    {90, 85, 88, 91}   // 第三个学生
};

// 访问第2个学生的第3门课成绩
cout << grades[1][2] << endl;  // 输出95

说白了,二维数组在内存里其实也是连续存储的,按行优先排列。你想想看,编译器只是帮我们把 grades[i][j] 转换成了 grades[i * 列数 + j] 这样的线性地址。

5.3 字符数组与C风格字符串

C风格字符串,其实就是以'\0'结尾的字符数组。这个'\0'是字符串结束标志,千万不能丢。

// 字符数组
char name[10] = {'Z', 'h', 'a', 'n', 'g', '\0'};

// 更简单的写法
char name[] = "Zhang";  // 自动在末尾加'\0'

// 注意:这个数组大小是6,不是5!
cout << "数组大小:" << sizeof(name) << endl;  // 输出6

重点:用双引号括起来的字符串字面量,末尾自动有'\0'。但用花括号初始化时,你得自己加。我见过有人写 char str[5] = "Hello";,这就有问题——"Hello"需要6个字符的空间(包括'\0'),数组只给了5个,编译能过但运行时会出问题。

C风格字符串常用函数(需要包含 <cstring>):

函数 作用 示例
strlen() 获取字符串长度(不含'\0') strlen("Hello") → 5
strcpy() 复制字符串 strcpy(dest, src)
strcat() 拼接字符串 strcat(dest, src)
strcmp() 比较字符串 相等返回0

避坑指南:我曾经在项目里用 strcpy 时没注意目标数组的大小,结果源字符串比目标数组长,直接覆盖了后面的内存。从那以后,我改用 strncpy 或者直接用C++的 string 类了。

5.4 string类:C++的现代方案

说实话,C风格字符串用起来挺麻烦的。C++标准库提供了 string 类,用起来舒服多了。你想想看,不用管内存管理,不用操心'\0',多省心。

#include <string>
using namespace std;

string name = "Zhang San";
string greeting = "Hello, " + name;  // 可以直接拼接

cout << "长度:" << name.length() << endl;  // 输出9
cout << "第一个字符:" << name[0] << endl;  // 输出Z

// 比较字符串
if (name == "Zhang San") {
    cout << "匹配成功" << endl;
}

string 类常用方法:

方法 作用 示例
length() / size() 获取字符串长度 s.length()
empty() 判断是否为空 s.empty()
substr(pos, len) 截取子串 s.substr(1, 3)
find(str) 查找子串位置 s.find("an")
c_str() 转成C风格字符串 s.c_str()

5.5 数组与字符串的遍历

遍历,说白了就是把每个元素都过一遍。最常用的就是for循环。

一维数组遍历

int scores[] = {85, 92, 78, 90, 88};
int size = sizeof(scores) / sizeof(scores[0]);

// 传统for循环
for (int i = 0; i < size; i++) {
    cout << scores[i] << " ";
}

// C++11 范围for循环(推荐)
for (int score : scores) {
    cout << score << " ";
}

二维数组遍历

int grades[3][4] = {
    {85, 92, 78, 90},
    {88, 76, 95, 82},
    {90, 85, 88, 91}
};

// 双重循环遍历
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        cout << grades[i][j] << " ";
    }
    cout << endl;
}

字符串遍历

string str = "Hello";

// 用索引遍历
for (int i = 0; i < str.length(); i++) {
    cout << str[i] << " ";
}

// 用范围for遍历(更简洁)
for (char ch : str) {
    cout << ch << " ";
}

小技巧:遍历时如果要修改元素,范围for循环要用引用:for (int &score : scores)。不加引用的话,你改的是副本,原数组不变。这个坑我踩过,当时改了半天发现数组值没变,差点怀疑人生。

数组与字符串知识体系 数组与字符串 一维数组 int arr[5]; 索引从0开始 连续内存存储 二维数组 int mat[3][4]; 行优先存储 表格数据结构 C风格字符串 char str[] = "Hi"; 以'\0'结尾 strcpy/strcat/strcmp string类 string s = "Hi"; 自动管理内存 支持+拼接、比较 遍历:for / 范围for

嗯,到这里数组和字符串的基础知识就讲完了。记住一点:能用 string 就别用C风格字符串,能用范围for循环就别用传统for循环。这些习惯能帮你省下不少调试时间。

公众号:蓝海资料掘金营,微信deep3321