在 C++ 里,长度会变、需要增删的序列通常用 std::vector(见vector)。内置数组长度在编译期固定,仍会出现在底层接口、固定大小缓冲或与 C 语言交互的场合,需要弄清它的布局和下标规则。本节先讲内置数组,再对比何时改用 vector 或 std::array。
内置数组:声明与初始化
#include <iostream>
int main() {
int scores[5] = {90, 85, 88, 0, 0};
scores[3] = 92;
for (int i = 0; i < 5; ++i) {
std::cout << scores[i] << " ";
}
std::cout << std::endl;
return 0;
}输出:
90 85 88 92 0 int scores[5] 定义了有 5 个元素的数组,下标从 0 到 4。scores[3] = 92 就是给第 4 个元素赋值 92,这个元素原本默认是 0,数组最后一个元素依旧是 0。如果写 scores[5] 就属于数组越界,编译器大多不会报错,程序运行结果不可预测。
用大括号给数组赋初值时,里面写的数值个数不能超过数组元素总数。数值写少了,剩下的 int 元素会自动填 0。函数里面定义的局部数组,如果没有写任何初始化,里面存的是内存里原本的随机数据,读取之前必须手动赋值。
int data[] = {1, 2, 3, 4}; // 长度由列表推导为 4
int zeros[10] = {}; // 全部为 0数组长度与 sizeof
int data[] = {1, 2, 3, 4};
std::size_t n = sizeof(data) / sizeof(data[0]); // 4对数组对象做 sizeof 得到整块字节数,再除以单个元素大小就是个数。数组名在多数表达式中会退化为指向首元素的指针,此时 sizeof 只是指针大小,不能再用来求元素个数。
传给函数时,形参写成 int a[] 实际是指针,须同时传入长度:
void print(const int a[], int n) {
for (int i = 0; i < n; ++i) {
std::cout << a[i] << " ";
}
}
int xs[] = {1, 2, 3};
print(xs, 3);只传数组名、不传 n,函数内无法由形参恢复长度。若要在函数里保留完整数组类型,可使用引用到数组,见指针章节。
多维数组(入门)
二维数组可视为数组的数组,按行优先连续存储。grid[1][2] 是第 2 行第 3 列:
#include <iostream>
int main() {
int grid[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
std::cout << grid[1][2] << std::endl;
for (int r = 0; r < 2; ++r) {
for (int c = 0; c < 3; ++c) {
std::cout << grid[r][c] << " ";
}
std::cout << "\n";
}
return 0;
}输出:
6
1 2 3
4 5 6 第一行打印的是 grid[1][2],即 6。后面两行按行列下标把整张表打出来。作为函数形参时,内层长度要写在类型里,例如 int a[][3]。
边界与安全
| 问题 | 说明 |
|---|---|
| 下标越界 | 标准不检查;可能破坏相邻内存或使程序异常结束 |
| 变长栈数组 | int n; int a[n]; 不是标准 C++,部分编译器当作扩展,换编译器可能失败;动态长度用 vector |
| 数组与 string | C 风格字符数组以 '\0' 结尾;日常文本优先 std::string |
访问前保证 0 <= index < size。需要插入、删除或运行时改变长度时用 std::vector。长度固定又希望有 size() 时,可用 std::array:
#include <array>
std::array<int, 5> a = {1, 2, 3, 4, 5};
std::cout << a.size(); // 5与范围 for 的配合
内置数组在范围 for 中绑定到整个数组类型,从而遍历全部元素(见范围 for)。退化后的指针无法单独用范围 for 得知长度,须保留数组对象本身。
本节说明了内置数组的声明、初始化与边界意识。下一节介绍std::string:可变长度字符串的操作与接口。