核心要点速览

  • 数组名:本质是首元素地址,仅 3 种场景不退化(sizeof&数组名decltype
  • 数组传参:一维退化为指针(丢失长度),二维必须指定第二维长度
  • C 风格字符串:以'\0'结尾的字符数组,strlensizeof计算逻辑不同
  • 数组 vs vector:数组编译期定长、手动管理;vector 动态扩容、自动管理

一、数组名与指针

1. 特性

数组名 通常等价于首元素地址,仅 3 种场景保留数组类型特性(不退化)。

2. 数组名不退化的 3 种场景

  • sizeof(数组名):计算整个数组的字节总大小(非指针大小)
  • &数组名:获取指向 “整个数组” 的指针(类型为数组类型(*)[长度]
  • decltype(数组名):推导为数组类型(如decltype(arr)int[5]

3. 数组初始化(C++11+)

  • 聚合初始化简化:可省略等号,多维数组支持部分初始化
1
2
int arr[] {1,2,3};          // 等价于 int arr[] = {1,2,3}
int mat[2][3] {{1,2}, {3}}; // 剩余元素自动补0
  • 零初始化技巧:空大括号或单 0 即可全局置 0
1
2
int arr[5] {0}; // 所有元素为0
int arr[5] {}; // C++11+ 空大括号等价上式(局部数组需显式指定)
  • 禁止窄化转换:聚合初始化不允许隐式窄化(普通初始化仅警告)
1
int arr[] {1.2}; // 编译报错(double→int 窄化)

4. 易错:&数组名 vs &数组首元素

1
2
3
int arr[5] = {1,2,3,4,5};
&arr; // 类型 int(*)[5],+1 偏移 5 个 int(20 字节)
&arr[0]; // 类型 int*,+1 偏移 1 个 int(4 字节)

二、数组作为函数参数

数组传参 必然退化为指针,丢失原数组长度,需手动传递长度参数。

1. 一维数组传参(3 种写法,等价)

1
2
3
4
5
6
7
8
// 写法1:数组形式([]内数字无意义,仅语法兼容)
void func1(int arr[5]) { ... }

// 写法2:省略长度的数组形式
void func2(int arr[]) { ... }

// 写法3:显式指针形式(推荐,最直观)
void func3(int* arr) { ... }

2. 二维数组传参

  • 二维数组退化后是 “指向一维数组的指针”,第二维长度不可省略(编译器需计算步长)
1
2
3
4
5
// 正确写法:指定第二维长度 3
void func(int mat[][3], int rows) { ... }

// 错误写法:第二维长度省略(编译报错)
void func(int mat[][], int rows, int cols) { ... }

3. 补充

  • 函数内无法通过sizeof(arr)获取原数组长度(此时arr已退化为指针,结果为 4/8 字节)
  • 必须显式传递长度(如func(arr, 5)),或通过数组结尾标记获取长度

三、字符串与字符数组(C 风格)

1. 本质

'\0'(空字符)结尾的字符数组,'\0'是字符串结束标记(字符串常量后编译器自动填充)。

2. 区别:strlen vs sizeof

函数 / 运算符 计算逻辑 是否包含'\0'
strlen(str) 统计'\0'之前的字符个数
sizeof(str) 计算整个字符数组的字节大小 是(含未使用空间)

3. 易错点

未添加'\0'会导致strlen越界:

1
2
char str1[] = "hello"; // 自动补'\0',数组长度 6(h e l l o \0)
char str2[] = {'h','e','l','l','o'}; // 无'\0',strlen(str2) 结果不确定

4. 字符串常量 vs 字符数组

类型 存储区域 可修改性 风险点
char* str = "abc" 常量区(只读) 不可修改 试图修改触发未定义行为
char str[] = "abc" 栈区 / 全局区 可修改 可直接修改元素(如str[0]='x'

四、数组 vs vector(动态数组)对比表

对比维度 数组(Array) vector(动态数组)
大小特性 编译期固定,运行时不可修改 运行时动态调整,push_back自动扩容
内存管理 栈 / 全局区存储,手动管理(无自动释放) 堆内存存储,自动管理(析构时释放资源)
长度获取 手动计算:sizeof(arr)/sizeof(arr[0]) 内置size()方法(直接获取元素个数)
传参方式 退化为指针,需额外传递长度 传引用(vector<int>&),保留长度信息
越界安全性 无检查,越界为未定义行为 at()方法支持越界检查(抛异常),更安全
灵活性 低(不可扩容 / 缩容) 高(支持插入、删除、扩容等操作)