核心要点速览
- 数组名:本质是首元素地址,仅 3 种场景不退化(
sizeof、&数组名、decltype)
- 数组传参:一维退化为指针(丢失长度),二维必须指定第二维长度
- C 风格字符串:以
'\0'结尾的字符数组,strlen与sizeof计算逻辑不同
- 数组 vs vector:数组编译期定长、手动管理;vector 动态扩容、自动管理
一、数组名与指针
1. 特性
数组名 通常等价于首元素地址,仅 3 种场景保留数组类型特性(不退化)。
2. 数组名不退化的 3 种场景
sizeof(数组名):计算整个数组的字节总大小(非指针大小)
&数组名:获取指向 “整个数组” 的指针(类型为数组类型(*)[长度])
decltype(数组名):推导为数组类型(如decltype(arr)为int[5])
3. 数组初始化(C++11+)
- 聚合初始化简化:可省略等号,多维数组支持部分初始化
1 2
| int arr[] {1,2,3}; int mat[2][3] {{1,2}, {3}};
|
1 2
| int arr[5] {0}; int arr[5] {};
|
- 禁止窄化转换:聚合初始化不允许隐式窄化(普通初始化仅警告)
4. 易错:&数组名 vs &数组首元素
1 2 3
| int arr[5] = {1,2,3,4,5}; &arr; &arr[0];
|
二、数组作为函数参数
数组传参 必然退化为指针,丢失原数组长度,需手动传递长度参数。
1. 一维数组传参(3 种写法,等价)
1 2 3 4 5 6 7 8
| void func1(int arr[5]) { ... }
void func2(int arr[]) { ... }
void func3(int* arr) { ... }
|
2. 二维数组传参
- 二维数组退化后是 “指向一维数组的指针”,第二维长度不可省略(编译器需计算步长)
1 2 3 4 5
| 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"; char str2[] = {'h','e','l','l','o'};
|
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()方法支持越界检查(抛异常),更安全 |
| 灵活性 |
低(不可扩容 / 缩容) |
高(支持插入、删除、扩容等操作) |