本课时有配套视频讲解,购买后即可观看(永久有效)
一维数组
一、课上练习
编程练习
二、知识总结
✨ 什么是数组
在之前的学习中,我们用变量来存储数据。但如果需要存储全班30个同学的成绩,定义30个变量就太麻烦了。数组就是用来解决这个问题的——它可以用一个名字存储多个同类型的数据,并且在定义时需要确定数组的大小。
你可以把数组想象成一排编了号的储物柜:每个柜子存放一个数据,通过编号(下标)就能快速找到对应的数据。
| 下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素 | arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
| 值 | 10 | 20 | 30 | 40 | 50 |
| 地址 | 0x1000 | 0x1004 | 0x1008 | 0x100C | 0x1010 |
数组在内存中是连续存储的。因为每个 int 占 4 字节,所以相邻元素的地址相差 4。知道数组首地址和下标,就能直接算出任意元素的地址(首地址 + 下标 × 4),这就是数组能通过下标快速访问元素的原因。
✨ 一维数组与多维数组
数组按照维度可以分为一维数组和多维数组:
- 一维数组:数据排成一行,只需要一个下标就能定位元素,就像上面的储物柜例子——
arr[3]就能找到第4个柜子。 - 多维数组:数据排成多行多列,需要多个下标才能定位元素,就像教室里的座位——需要"第几排、第几列"两个编号才能确定位置。
本节课先学习一维数组,多维数组会在后面的课程中学到。
✨ 一维数组的定义
定义一维数组时,需要说明数据类型、数组名称和数组大小。其中数组的命名规则和变量的命名规则相同,数组的大小用[]加数字的形式表述。
一维数组定义代码示例
int arri[10];
double arrd[1];
bool arrb[12];由于竞赛时有内存限制,所以建议在主函数外定义数组。
关于一维数组的大小,有几点需要额外注意:
- 数组的大小必须在程序编译时已经知道,即不可以通过读入一个数据,然后根据读入数据的方式进行创建。
错误的数组定义代码示例
int n;
cin >> n;
int arr[n]; //不要通过这种形式定义一个数组- 一般我们直接使用数字表示数组大小,有时候当我们需要创建多个大小相同的数组时,我们会使用常量表示数组大小。
使用常量定义数组大小代码示例
int N = 100000;
int arr1[N];
double arr2[N];- 数组大小必须使用整数表示,但是因为字符型实际也是以整数的形式存储的,所以也可以使用字符型来定义数组大小,但是平时请不要使用这种方式进行数组定义。
字符型定义数组大小代码示例
int arr['0']; //创建一个大小为48的数组✨ 一维数组的初始化
序列初始化
使用{}的方式对一维数组进行初始化,必须在定义数组时对其进行初始化。若{}内填写数值,则按照顺序对数组内元素进行赋值,未填写部分补0。
序列初始化代码示例
int arr[10] = {}; // 初始化为全0
int arr[5] = {1, 2}; // 将第一个数初始化为1,第二个数初始化为2,其余所有数初始化为0
int arr[5] = {0}; // 将第一个数初始化为0,其余所有数初始化为0
int arr[5] = {2, 3, 8, 1, 3}; // 整个数组初始化为2,3,8,1,3fill_n初始化
需要添加头文件,可以在任意位置对数组进行赋值,将数组元素全部设置为某一个值。使用fill_n需要填写三部分内容:数组名称、数据个数和初始化的数值,初始化的数值可以为任意值。
fill_n初始化代码示例
1#include <iostream>
2#include <algorithm>
3using namespace std;
4
5int arr[105];
6
7int main () {
8 fill_n(arr, 105, 1);
9 return 0;
10}memset初始化
memset是C语言风格的初始化方式,可以在任意位置对数组进行赋值,将数组元素全部设置为某一个值。
使用memset需要填写三部分内容:数组名称、初始化数值和数组大小。
其中初始化的数值一般使用16进制表示,常用数值为:0、1、-1、0x3f、0x7f,其中0x3f和0x7f都表示一个比较大的整数。
另外数组大小不是数组里面存储的数值的个数,而是占用内存的大小。
memset初始化代码示例
1#include <iostream>
2#include <algorithm>
3using namespace std;
4
5int arr[105];
6
7int main () {
8 memset(arr, 0, sizeof(arr));
9 memset(arr, 1, sizeof(arr));
10 memset(arr, -1, sizeof(arr));
11 memset(arr, 0x3f, sizeof(arr));
12 memset(arr, 0x7f, sizeof(arr));
13 return 0;
14}将数组初始化为全0的几种方式
将数组初始化为全0有以下5种方式:
arr[3] = {};arr[3] = {0};arr[3] = {0, 0, 0};fill_n(arr, 3, 0);memset(arr, 0, sizeof(arr));
✨ 一维数组元素的访问
一维数组只需要一个下标就能定位元素。我们使用数组名称[下标]的形式访问数组,可以通过该方式获取到下标位置的元素的值或者对该位置进行赋值。
数组元素访问代码示例
1#include <iostream>
2#include <algorithm>
3using namespace std;
4
5int arr[105];
6
7int main () {
8 fill_n(arr, 105, 1);
9 arr[10] = 2; //对某个位置进行赋值
10 arr[20] = 4; //对某个位置进行赋值
11 for (int i = 0; i < 105; i++) { // 使用循环遍历数组的所有下标
12 cout << arr[i] << " "; // 获取某个位置的数值
13 }
14 return 0;
15}访问数组元素需要注意以下几点:
- 下标从0开始,到数组个数-1结束,即如果数组的元素个数为n,第一个元素对应的下标为0,第二个元素对应的下标为1,最后一个元素对应的下标为n-1。
- 数组下标不可以为负数,如果使用
arr[i-1]这样的操作时,一定要注意检查i-1是否大于等于0。 - 数组下标不可以越界使用,即如果数组的元素个数为n,不能使用超过n-1的下标,如果使用
arr[i+1]这样的操作时,一定要注意检查i+1是否小于n。 - 对于越界使用数组下标的情况,编译器不会报错,但是程序运行时会出现错误,所以必须谨慎使用。
✨ 一维数组的常见错误
- 数组越界访问:这是最常见也是最危险的错误。数组大小为n,下标范围是0到n-1,访问arr[n]就已经越界了。编译器不会报错,但运行时可能得到错误结果甚至程序崩溃。
越界访问的典型场景
int arr[5] = {1, 2, 3, 4, 5};
// 错误:访问了 arr[5],下标最大只能到4
for (int i = 0; i <= 5; i++) {
cout << arr[i];
}- 在函数内定义大数组导致栈溢出:在main函数或其他函数内部定义大数组(如
int arr[1000000];)会导致栈溢出。解决方法是将大数组定义在主函数外部(全局变量区)。 - 用变量作为数组大小:
int n; cin >> n; int arr[n];这种写法在某些编译器上能通过,但它不是标准C++的做法,在竞赛中应该避免。正确做法是预先定义一个足够大的数组。 - 忘记初始化数组:全局数组会自动初始化为0,但局部数组不会。局部数组如果不初始化,里面的值是随机的垃圾数据。
- memset初始化的误用:memset是按字节填充的,所以
memset(arr, 1, sizeof(arr))并不会将每个int元素设为1,而是将每个字节设为1,得到的int值是16843009。memset只适合初始化为0、-1或特殊值(0x3f、0x7f)。