📌 阅读时长:25分钟 | 关键词:C++、数组、二维数组、C风格字符串、std::string、结构体、枚举
引言
上一篇文章我们学会了用指针和引用来操控单个变量。但现实中,我们往往要处理一堆数据------全班 50 个学生的成绩、一段文本、一个商品的信息。这篇就来学习 C++ 组织数据的核心工具:数组、字符串和结构体。
一、一维数组:同类型数据的集合
1.1 声明与初始化
cpp
int scores[5] = {80, 90, 75, 85, 95}; // 完整初始化
int nums[5] = {1, 2}; // 部分初始化:剩余自动补 0
int data[] = {10, 20, 30}; // 省略大小,编译器自动推断为 3
1.2 下标访问与遍历
C++ 数组下标从 0 开始,访问越界会导致未定义行为(程序崩溃或数据错乱):
cpp
int arr[5] = {10, 20, 30, 40, 50};
// for 循环遍历
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
// 输出:10 20 30 40 50
计算数组元素的平均值:
cpp
int numbers[5] = {10, 20, 30, 40, 50};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += numbers[i];
}
double avg = static_cast<double>(sum) / 5;
std::cout << "平均值为: " << avg << std::endl; // 30
1.3 数组名 = 指向首元素的指针
数组名在大多数情况下会退化为指向首元素的常量指针:
cpp
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p 指向 arr[0]
std::cout << arr[2] << std::endl; // 3
std::cout << *(p + 2) << std::endl; // 3(等价写法)
| 表达式 | 含义 |
|---|---|
arr |
数组首元素地址 |
*arr |
首元素的值 |
arr + i |
第 i 个元素的地址 |
*(arr + i) |
第 i 个元素的值(等价于 arr[i]) |
二、二维数组与行指针
2.1 内存布局:按行优先
二维数组在内存中是按行连续 存储的,例如 int m[2][3]:
内存布局:m[0][0] m[0][1] m[0][2] m[1][0] m[1][1] m[1][2]
2.2 行指针:指向一行的指针
cpp
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
// matrix 退化为指向第一行的行指针(类型:int (*)[3])
int (*p_row)[3] = matrix;
std::cout << matrix[1][2] << std::endl; // 6
std::cout << *(*(p_row + 1) + 2) << std::endl; // 6(行指针方式)
// p_row + 1 → 跳过一整行(3个int),指向第二行
// *(p_row + 1) → 第二行的首地址(int* 类型)
// *(p_row + 1) + 2 → 第二行第 3 个元素的地址
// *(*(p_row + 1) + 2) → 第二行第 3 个元素的值
2.3 指针数组 vs 数组指针
这是一对容易混淆的概念------注意括号位置:
cpp
int *ptr_arr[5]; // 指针数组:5个指针的数组,每个指向int
int (*arr_ptr)[5]; // 数组指针:1个指针,指向包含5个int的数组
| 类型 | 本质 | 示例含义 |
|---|---|---|
int *p[5] |
数组 | 有 5 个 int* 元素的数组 |
int (*p)[5] |
指针 | 指向「含 5 个 int 的数组」的指针 |
三、字符串:从 C 风格到 std::string
3.1 C 风格字符串
C 风格字符串本质是字符数组 ,以空字符 \0 作为结束标志:
cpp
char str1[] = "hello"; // 自动在末尾加 '\0',数组大小为 6
char str2[6] = {'h', 'e', 'l', 'l', 'o', '\0'}; // 手动添加尾零
char str3[10] = "world"; // 剩余 4 个位置补 '\0'
std::cout << strlen(str1) << std::endl; // 5(不计算 '\0')
关于 '\0' 和 ' ' 的区别:
| 字符 | ASCII 码 | 含义 |
|---|---|---|
'\0' |
0 | 字符串结束标记(非打印字符),不计算长度 |
' ' |
32 | 普通空格字符(打印字符),计算长度 |
空字符串 "" 只包含一个 '\0',长度为 0。
3.2 std::string:现代 C++ 的首选
C 风格字符串容易出错(缓冲区溢出、忘记 \0),std::string 类自动管理内存,更安全方便:
cpp
#include <string>
std::string s1 = "hello";
std::string s2;
std::cin >> s2; // 输入字符串
std::cout << s1.length() << std::endl; // 获取长度:5
std::cout << s1 + " " + s2 << std::endl; // 字符串拼接:hello world
// 像访问数组一样访问单个字符
std::cout << s1[0] << std::endl; // 'h'
s1[0] = 'H'; // 修改
四、结构体:自定义你的复合数据类型
4.1 什么是结构体?
结构体允许你将不同类型的数据打包成一个有意义的整体:
cpp
struct Student {
std::string name; // 姓名
int id; // 学号
float score; // 成绩
};
4.2 创建和使用
cpp
Student s1;
s1.name = "张三";
s1.id = 2023001;
s1.score = 95.5;
std::cout << "姓名: " << s1.name << std::endl;
std::cout << "成绩: " << s1.score << std::endl;
4.3 C++ struct vs class
| 特性 | struct | class |
|---|---|---|
| 默认成员权限 | public | private |
| 默认继承方式 | public | private |
| 能包含函数 | ✅ 可以 | ✅ 可以 |
| 约定用法 | 简单数据集合 | 复杂行为封装 |
💡 语法上两者几乎等价,只是默认权限不同。平时用 struct 存数据,用 class 做封装。
五、枚举:给整数起个有意义的名字
枚举把一组整型常量变成有意义的名称,告别"魔法数字":
cpp
enum Weekday {
Monday, // 0
Tuesday, // 1
Wednesday, // 2
Thursday, // 3
Friday, // 4
Saturday, // 5
Sunday // 6
};
Weekday today = Wednesday;
if (today == Wednesday) {
std::cout << "今天星期三!" << std::endl;
}
// 输出:今天星期三!
也可以手动指定值:
cpp
enum Status { Success = 0, Warning = 10, Error = 20 };
小结
| 序号 | 知识点 | 一句话总结 |
|---|---|---|
| 1 | 一维数组 | 同类型数据连续存储,下标从 0 开始,越界会引发未定义行为 |
| 2 | 二维数组 | 按行优先存储,数组名退化为行指针 |
| 3 | 指针数组 vs 数组指针 | 看括号位置------*p[n] 是数组,(*p)[n] 是指针 |
| 4 | C 风格字符串 | 字符数组 + \0 结尾 |
| 5 | std::string | 现代 C++ 字符串,自动管理内存,推荐使用 |
| 6 | 结构体 | 把不同类型数据打包成一个整体 |
| 7 | 枚举 | 给整型常量起名,提高可读性 |
下一篇文章,我们将进入 C++ 的核心------函数进阶:重载、默认参数、内联函数、回调与递归等高级用法。
本文是「C++ 从基础到项目实战」系列的第 2 篇。关注我,不错过后续更新。