7. 数组基础:一维数组声明与初始化、数组遍历、数组越界问题
数组这东西,说白了就是一块连续的内存空间,用来存一堆相同类型的数据。我刚学C++那会儿,觉得数组太简单了,不就是个列表嘛。直到后来在项目里踩了越界的坑,才老老实实把这块啃透。
今天咱们就把一维数组的声明、初始化、遍历,还有那个让人头疼的越界问题,一次性说清楚。
7.1 一维数组的声明
声明数组其实就三步:告诉编译器你要存什么类型的数据、数组叫什么名字、要存多少个元素。
// 基本语法
类型 数组名[元素个数];
// 举个例子
int scores[5]; // 存5个int
double prices[10]; // 存10个double
char name[50]; // 存50个char
这里有个关键点:元素个数必须是常量。我见过不少新手这么写:
int n = 5;
int arr[n]; // 这样写,有些编译器能过,但这不是标准C++!
标准C++里,数组大小得是编译期就能确定的常量。如果你确实需要运行时才确定大小的数组,用 std::vector 才是正道。
7.2 数组的初始化
声明数组的时候,可以顺便给它赋初值。C++提供了好几种初始化方式,我挑常用的说。
7.2.1 完全初始化
int arr[5] = {1, 2, 3, 4, 5}; // 全部赋值
7.2.2 部分初始化
int arr[5] = {1, 2}; // 前两个是1和2,后面三个自动补0
嗯,这里要注意:没给值的元素,编译器会自动初始化为0。这个特性有时候挺有用,比如你想把整个数组清零:
int arr[100] = {0}; // 所有元素都是0
7.2.3 省略大小的初始化
int arr[] = {1, 2, 3, 4, 5}; // 编译器自动算出来大小是5
我个人挺喜欢这种写法,少算一个数,少一个出错的机会。不过要注意,只有在初始化的时候才能这么干。
7.3 数组的遍历
遍历数组,说白了就是把每个元素挨个儿过一遍。最经典的方式就是用 for 循环。
int arr[5] = {10, 20, 30, 40, 50};
// 传统for循环
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
// C++11 范围for循环(推荐)
for (int x : arr) {
cout << x << " ";
}
范围for循环是C++11引入的,写起来简洁,而且不会越界。我现在的项目里,只要不需要下标,一律用范围for。
但有时候你需要知道当前元素的下标,比如你要修改某个位置的元素,那就得用传统for了:
for (int i = 0; i < 5; i++) {
arr[i] = arr[i] * 2; // 每个元素翻倍
}
7.4 数组越界问题
好,接下来这个坑,我估计每个C++程序员都踩过。数组越界,就是访问了数组范围之外的内存。
int arr[3] = {1, 2, 3};
cout << arr[3]; // 越界!arr只有下标0、1、2
arr[5] = 100; // 越界!写到了别人的地盘上
为什么会这样?C++为了性能,不会在运行时检查数组下标是否合法。你写 arr[5],编译器就直接按地址偏移去访问那块内存了。至于那块内存里有什么,是别的变量还是系统数据,它不管。
越界的后果,轻则读到垃圾值,重则程序崩溃,甚至产生安全漏洞。所以记住几条铁律:
- 下标从0开始,到 size-1 结束
- 循环条件用 < 而不是 <=
- 不确定大小时,用 vector 代替原生数组
7.5 知识结构图
下面这张图,把一维数组的核心知识点串起来了。你想想看,从声明到初始化,再到遍历和避坑,其实就这几件事。
7.6 避坑指南
最后,把我这些年踩过的坑总结一下,你写代码的时候多留个心眼:
- 声明时别忘给大小:
int arr[];这种写法,编译器不知道要分配多少内存,直接报错。 - 初始化列表别超长:
int arr[3] = {1,2,3,4};元素比数组大,编译不过。 - 遍历时下标别越界:循环条件写成
i <= size是经典错误,记住是i < size。 - 数组名不是指针:虽然数组名可以当指针用,但
sizeof(arr)返回的是整个数组的字节数,不是指针大小。
sizeof(arr) / sizeof(arr[0]) 可以算出数组的元素个数。不过这只在数组定义的作用域内有效,传到函数里就不行了。那时候,老老实实把数组长度也传进去。
数组这块,说白了就是「声明-初始化-遍历-防越界」这四步。你写个三五次,自然就熟了。下次咱们聊二维数组,那个更贴近实际项目——比如图像处理、矩阵运算,都离不开它。
公众号:蓝海资料掘金营,微信deep3321