🔷 第七章 数组、指针和引用
📄 一句话概括

声明一个一维数组就一句话:类型 数组名[长度];。长度必须是编译期能确定的常量,声明完格子就分配好了,但里面是垃圾值(跟 2.3.2 的"未初始化"一模一样,只是从 1 格变成了一排)。

📝 本节和前面几节的关系

7.1 讲了"数组是什么、为什么要有"。这一节落地"怎么把一排柜子订出来"。写声明的零件都是旧朋友:类型来自第二章、数组名要守 2.3.1 的标识符规则、长度必须是常量、分号别忘了(1.3.2 的铁律)。下一节 7.1.2 解决"柜子订好了,怎么打开某一格"。

一、声明的格式

类型 数组名[长度];

三样东西各有讲究:

部分 写法 说明
类型 int / double / char 每个格子的规格,全数组统一
数组名 score 守标识符规则(2.3.1);别和变量重名
长度 [50] 格子的数量,写在方括号里(不是圆括号!)

几个合法的例子:

int    score[50];        // 50 个整数格
double price[100];       // 100 个小数格
char   name[20];         // 20 个字符格(7.3 的字符数组)

读完要能自动翻译成大白话:int score[50]; = "给我订一排 50 格的整数柜,整排叫 score"。

二、长度的三条硬规矩

规矩 1:长度必须是"常量表达式"

int  a[5];              // ✅ 字面量常量
const int N = 5;
int  b[N];              // ✅ const 常量(2.2.5 的 const,编译期已知)
#define M 5
int  c[M];              // ✅ 宏常量

int n = 5;
int  d[n];              // ❌ 标准 C++ 不允许:n 是"变量",值要到运行时才知道

为什么这么严?因为在 7.1 的内存图里,数组是一整块连续空间。编译器在生成代码时就得把这块空间的尺寸定死,才能算出后面的变量该放在哪。如果长度能随便变,整个内存布局就没法安排了。

小提示:Dev-C++ 里 int d[n]; 有时居然能编过(g++ 的"变长数组"扩展),但这是非标准行为,换个编译器就报错。别依赖它。

规矩 2:长度是"格数",不是"最大下标"

int a[5];        // 5 格: a[0] a[1] a[2] a[3] a[4]

[5] 是格子数量。合法下标是 0~4,没有 a[5]。写 [5] 然后去用 a[5],是新手最常犯的错(7.1.2 详述"越界")。

规矩 3:声明 ≠ 摆放

int score[5];    // 只订了柜子,格子是空的(垃圾值)

语句执行完,格子里是什么完全不确定。有的编译器(Debug 模式)会顺手刷成 0,有的直接留着垃圾值。"在我这儿是 0"是最危险的错觉——换台机器、换个模式就变了。想让它干净,看 7.1.3 的初始化。

三、声明完能立刻问的三个问题

int a[5];

sizeof(a)                 // 20:整排占多少字节
sizeof(a[0])              // 4 :一格占多少字节
sizeof(a) / sizeof(a[0])  // 5 :一共几格(不写死数字的标准写法)

最后一行是本节最实用的技巧。以后数组长度可能改(从 5 改成 50),遍历时写 i < sizeof(a)/sizeof(a[0]) 就不用跟着改数字,改一处全生效——这正是 6.1 说的"函数/常量的价值:改一处、全生效"。

四、动手 Demo:量一量自己订的柜子

#include <iostream>
using namespace std;

int main() {
    int    score[5];
    double price[4];
    char   name[13];

    cout << "score 整排字节 = " << sizeof(score) << endl;
    cout << "score 一格字节 = " << sizeof(score[0]) << endl;
    cout << "score 一共格数 = " << sizeof(score) / sizeof(score[0]) << endl;
    cout << "price 一共格数 = " << sizeof(price) / sizeof(price[0]) << endl;
    cout << "name  一共格数 = " << sizeof(name)  / sizeof(name[0])  << endl;
    return 0;
}

看到什么算成功:依次输出 20、4、5、4、13。

读结果:int 一格 4 字节(2.3.3),排 5 格就是 20;double 一格 8 字节,排 4 格就是 32 字节(所以格数是 32/8 = 4,不是 4 字节);char 一格 1 字节,13 格就是 13 字节。

30 秒实验:把 int score[5]; 改成 int score[50];,重新运行——每一个数字自动变成 10 倍,输出 200、4、50。全程只改了一处。

五、常见误区

  1. "方括号里写的是最大下标"——写的是格子数量。int a[5] 的合法下标是 0~4。记住口诀:写数量、用数量减一。
  2. "int n = 5; int a[n]; 我这儿能编过啊"——那是 g++ 的扩展,不是标准 C++。换成 MSVC 或加编译参数就报错。const 才是正路。
  3. "声明完格子里是 0"——不保证。未初始化=垃圾值(2.3.2 的老朋友),只是现在垃圾有 5 份。要干净就用 7.1.3 的初始化。
  4. "数组名可以随便换"——数组名不是普通变量。它代表"整排柜子的首地址",不能 a = b; 整体赋值,也不能 a++;。7.5.1 会讲清这条的来由。
自测三问(点开对答案) 1. `int a[10];` 一共几格?合法下标范围?——10 格;`0~9`。 2. 为什么长度必须是常量?——数组是一整块连续空间,编译器必须在编译期定死尺寸才能安排内存布局。 3. `sizeof(a) / sizeof(a[0])` 有什么用?——不写死数字地数出数组格数,将来改长度不用改循环。

相关笔记