基础
C++ 数组:完整图景
一句话答案:int a[5] 保留五个连续的 int 位置,命名为 a[0] … a[4]。长度在编译时固定,而 C++ 不会在运行时检查你的下标。
内存是一排,不是网格
数组是一整块连续内存。a[3] 不是按名字查表 —— 它是「块首地址 + 三个元素宽度的字节」。这就是下标访问是 O(1) 的原因:一次乘法、一次加法。
#include <iostream>
using namespace std;
int main() {
int a[5] = {10, 20, 30, 40, 50};
cout << "first: " << a[0] << endl;
cout << "third: " << a[2] << endl;
cout << "last: " << a[4] << endl;
return 0;
}
并不存在 a[5]。写出来能编译、能运行,读到的只是内存里紧挨着的下一个东西 —— 那是未定义行为,不是一条错误信息。
为什么从 0 开始
因为下标本质是偏移量。第一个元素距离块首是零格。从 1 开始的语文要在每次访问前减一;C++ 把这次减法转移到了你的代码里,于是运行时免费。
开多大:取上限,而不是精确值
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int a[100]; // 开一个上限
for (int i = 0; i < n; i++) cin >> a[i];
cout << "read " << n << " values" << endl;
return 0;
}
C++ 数组不能改大小。真实代码里要么开一个宽裕的上限(如上),要么用会自动增长的 std::vector。两者对比见vector 与数组。
值得背下来的三个模式
1. 求和 —— 累加器初始化为零。
#include <iostream>
using namespace std;
int main() {
int a[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) sum += a[i];
cout << sum << endl; // 15
return 0;
}
2. 求最大 —— 用第一个元素做种子,绝不用 0。
#include <iostream>
using namespace std;
int main() {
int a[4] = {-9, -3, -7, -1};
int best = a[0];
for (int i = 1; i < 4; i++) {
if (a[i] > best) best = a[i];
}
cout << best << endl; // -1,不是 0
return 0;
}
3. 计数 —— 每类一个计数器。
#include <iostream>
using namespace std;
int main() {
int a[6] = {4, 7, 10, 3, 8, 1};
int even = 0, odd = 0;
for (int i = 0; i < 6; i++) {
if (a[i] % 2 == 0) even++;
else odd++;
}
cout << "even " << even << ", odd " << odd << endl;
return 0;
}
二维数组是「行的行」
#include <iostream>
using namespace std;
int main() {
int g[3][2] = {{1, 2}, {3, 4}, {5, 6}};
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 2; c++) {
cout << g[r][c] << " ";
}
cout << endl;
}
return 0;
}
第一个下标永远是行。写反了会悄悄把你的数据转置 —— 矩阵转置 练的就是这个坑。
不会自我宣告的 bug
- 差一。
i <= n会读到a[n],越界一格。既不保证崩溃,也不给警告。 - 开得太小。
int a[10]装 11 个值,会覆盖相邻内存。 - 累加器没初始化。
int sum; sum += a[i];从垃圾值开始加。 - 最大值用 0 做种子。正数输入没问题,全负数就错 —— 抓它的测试在数组最大值里。