C/C++ 入门:数组

变量一次只能保存一个值,要存”一组同类型的数据”,最简单的办法就是数组(array)。

📚 基本概念速读

名称 定义 省流
数组 一段连续内存里存放的多个同类型元素 同类型数据排队
元素(element) 数组里的每一个数据 队伍中的一员
下标(index) 元素在数组里的位置编号,从 0 开始 第几个
数组长度 数组能容纳的元素个数 队伍长度
多维数组 数组的元素还是数组,比如二维数组 表格
数组名 数组的名字,使用时通常退化为首元素地址 队伍的队首

🧩 数组的基本模型

数组把多个同类型变量放进一段连续的内存里,通过下标访问:

1
int scores[5];   // 5 个 int 排成一行

可以这样理解:

1
2
3
下标:  [0]   [1]   [2]   [3]   [4]
数据: 92 88 95 70 85
地址: +----+----+----+----+----+


flowchart LR
    A[数组 scores] --> B[下标 0]
    A --> C[下标 1]
    A --> D[下标 2]
    A --> E[...]
    A --> F[下标 n-1]

数组的两个基本事实:

  1. 同类型:所有元素类型一致,占用内存大小相同;
  2. 连续内存:元素紧挨着存放,所以按下标访问是 O(1) 的,非常快。

🧮 一维数组

定义与初始化

1
2
3
4
5
6
7
8
9
int a[5];                  // 定义:5 个 int,未初始化(值不确定)

int b[5] = {1, 2, 3, 4, 5}; // 定义并初始化

int c[5] = {1, 2}; // 部分初始化:剩下补 0,即 1 2 0 0 0

int d[] = {1, 2, 3}; // 省略长度:编译器数出 3 个

int e[3] = {}; // 全部初始化为 0

数组长度必须是编译期能确定的常量(普通写法里不能是运行时变量):

1
2
3
4
int n = 5;
// int arr[n]; // 错误或不可移植:长度不是常量表达式
const int N = 5;
int arr[N]; // 可以:N 是常量

需要”运行时才知道大小”的数组,用第 9 章的 new[]vector,不要用变长数组写法。

下标访问

下标从 0 开始,最后一个元素的下标是长度减一:

1
2
3
4
5
int scores[5] = {92, 88, 95, 70, 85};

scores[0] = 90; // 修改第一个元素
cout << scores[0] << endl; // 90
cout << scores[4] << endl; // 85,最后一个

遍历

1
2
3
for (int i = 0; i < 5; i++) {
cout << scores[i] << " ";
}

也可以结合数组长度求法:

1
int n = sizeof(scores) / sizeof(scores[0]);   // 5:总字节数 / 单个元素字节数

📦 数组与函数

数组传给函数时,传的是首元素地址(数组名退化),函数里拿不到数组长度,需要额外传:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

// 形参 int arr[] 和 int* arr 等价,都是指针
void printArray(int arr[], int size)
{
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}

int main()
{
int scores[5] = {92, 88, 95, 70, 85};
printArray(scores, 5); // 传数组名 + 长度
return 0;
}

因为传的是地址,函数里修改数组会直接影响原数组

1
2
3
4
5
6
void addOne(int arr[], int size)
{
for (int i = 0; i < size; i++) {
arr[i] += 1; // 修改的是原数组
}
}

🗺️ 多维数组

二维数组可以理解成”数组的数组”,适合表示表格、矩阵:

1
2
3
4
5
6
7
8
int matrix[3][4] = {           // 3 行 4 列
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};

cout << matrix[0][0] << endl; // 1,第 1 行第 1 列
cout << matrix[2][3] << endl; // 12,第 3 行第 4 列

双重循环遍历:

1
2
3
4
5
6
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 4; col++) {
cout << matrix[row][col] << " ";
}
cout << endl;
}


flowchart TD
    A[matrix 3 行 4 列] --> B[第 1 行: 1 2 3 4]
    A --> C[第 2 行: 5 6 7 8]
    A --> D[第 3 行: 9 10 11 12]

三维及以上同理,只是现实中用得较少。

⚠️ 越界与常见错误

错误 后果
arr[5](长度 5 的数组) 越界访问,未定义行为,可能读到脏数据或崩溃
忘记初始化就用 读到不确定的值
数组长度用运行时变量 变长数组写法不可移植
函数里只传数组名不传长度 函数不知道数组多大,容易越界

C/C++ 不检查数组越界,编译期通常不报错,运行时才会出问题。这是数组和 vector 最大的差别之一——vectorsize(),还能用 at() 检查越界。

现代 C++ 中,普通场景优先用 std::vector(第 9 章讲过);数组仍然常见于固定大小、追求极简的场景,以及和 C 代码打交道时。

⚠️ 常见误区

误区 正解
下标从 1 开始 C/C++ 下标从 0 开始
数组越界会报错 不检查越界,是未定义行为
传数组给函数是拷贝 传的是首元素地址,函数内修改影响原数组
数组名可以随便赋值 数组名是常量地址,不能整体赋值
sizeof(arr) 在函数里也能拿到长度 函数里 arr 是指针,sizeof 得到的是指针大小

✅ 总结

数组把同类型数据放进连续内存,按下标 O(1) 访问;下标从 0 开始、不检查越界、传参时退化为指针,这三个特性决定了它的用法和风险。

学完数组,下一步的指针、字符串会大量建立在”数组名是首元素地址”这个事实上;而需要动态大小、自动管理内存时,记得有 vector 这个更省心的选择。

Happy Hacking! 🎉