目录
1. 数组的概念
数组由数据类型相同的一系列元素组成(是一组相同类型元素的集合)。
- 一系列:1个或者多个;即数组元素个数不能为0
- 类型相同:数组中存放的数据,类型都是相同的。
需要使用数组时,通过声明告诉编译器,数组内含有多少个元素和这些元素的类型。
数组分为一维数组和多维数组,一般多维数组就是指二维数组。
2. 一维数组的创建和初始化
2.1 数组的创建
普通变量使用的数据类型,数组都是可以使用的,考虑下面的数组:
int math_socer[20];// 整型数组
char name[]; // 字符数组
double rain[50]; // 浮点型数组
- 方括号([ ])表明 math_socer、name、rain 都是数组,方括号中的数字表明数组中的元素个数。由此可见,方括号中的数字必然为正整数。
- 数组在创建的时候,可以指定数组的类型( int、char、double等等)和数组的大小以及数组的名字(name、rain等等)。
数组创建好了,但很明显,现在数组内的值都是随机值。
2.2 数组的初始化
数组通常被用来储存程序需要的数据,这个时候在程序一开始就初始化数组比较好。以下是数组初始化的方法:
数组的初始化一般使用大括号,将数据放在大括号中,用逗号分隔数据。
数组如果进行了初始化,数组的大小就可以省略掉。
#include <stdio.h>
int main()
{
int arr1[5] = { 1,2,3,4,5 }; // 完全初始化
int arr2[] = { 1,2,3,4,5 }; // 如果数组初始化了,方括号内的数字可省略。会自动计算数组大小
int arr3[5] = { 1, 2 }; // 未完全初始化 -- 剩余元素会自动成为0
char arr4[] = "hello";
char arr5[] = { 'h','e','l','l','o' };
// 这两种初始化方式是一样的,只有字符数组可以这样。
int arr6[5] = { 1,2,3,4,5,6 }; // 超出数组所能接受的范围,这是错误的示范-- 初始化值太多
return 0;
}
以上是一些初始化的方式与错误。
2.3 数组的类型
去掉数组名就是数组的类型。
注意:数组名前面的类型是数组内元素的类型,切记不要混淆。
2.2节的代码为例:
(数组类型)int [5] -- arr1(数组名)
(数组类型)int [] -- arr2(数组名)
其余类似。
3. 一维数组的使用
3.1 数组的下标
C语言规定数组是有下标的,下标是从0开始的,我们可以通过下标来访问数组中的元素。
// 数组元素的访问
#include <stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
printf("%d", arr[0]); // 1
printf("%d", arr[1]); // 2
printf("%d", arr[2]); // 3
printf("%d", arr[3]); // 4
return 0;
}
C语言中提供了一个操作符[ ],这个操作符叫做:下标引用操作符。 (数组名 + 方括号 + 数字)
3.2 数组的输入输出
我们总不能一直一个一个元素访问吧,那多麻烦。那么如何访问整个数组的元素呢?-- for循环
// 访问整个数组的内容
// 正着打印 把条件改一改就可以逆着打印
#include <stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
// 1 2 3 4
for循环产生了0~4的下标,一次又一次打印,最终结果为:1 2 3 4
初始化后的数组我们会打印了,就可以自己输入数组的元素来打印了。
// 自己输入数据并打印出来
#include <stdio.h>
int main()
{
int arr[5];
for (int i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[j]);
}
return 0;
}
// 1 2 3 4 5
// 1 2 3 4 5
就这么简单。可以自己动手试一试。
4. 一维数组在内存中的储存
数组的基本知识学完,我们继续深入了解数组。首先,我们先来了解一下数组在内存中的储存。
// 打印数组元素的地址
#include <stdio.h>
int main()
{
int arr[5] = { 0 };
for (int i = 0; i < 5; i++)
{
printf("arr[%d] = %p\n", i, &arr[i]);
// & -- 取出地址
}
return 0;
}
运行该代码:
从输出的结果分析,随着数组下标的增长,地址是由小到大变化的,且每次增加4(因为整型是4个字节)。这里有小伙伴可能不知道C是啥意思,这是十六进制数(123456789ABCDEF)(即8+4=C,C+4=0,剩余的大家就懂了)。
因此,我们得出结论:数组在内存中是连续存放的。至于了解这个有啥用,等学习到后面的指针自然会知道。(我们只要知道了一个元素的地址,其余元素就可以顺藤摸瓜的找到了)
5. sizeof 计算数组元素个数
在第四小节中,我们只打印了5个元素,如果我们修改了初始化的元素个数,我们就需要改for循环中判断条件,这样是不是有点复杂,那么有没有方法把判断条件和元素个数联系起来。
答案是:有的,兄弟。可以用sizeof。
sizeof是C语言中的关键字,用来计算类型或者变量大小的。那么,就可以用来计算数组的大小。
// sizeof计算数组的大小
#include <stdio.h>
int main()
{
// 如何计算一个数组的元素个数
// sizeof(arr)计算的是整个数组的大小 20 * 4 = 80
// sizeof(arr[0])计算一个元素的长度
// sizeof(arr) / sizeof(arr[0]) 计算数组的元素个数
int arr[20] = { 0 };
long long sz = sizeof(arr) / sizeof(arr[0]);
printf("%zd\n", sz); // sizeof 返回类型为size_t类型, 所以用%zd
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
// 20
// 20个0
这样子一搞,不管数组怎么变化,计算出的大小也随着变化。
6. 二维数组的创建
6.1 二维数组的概念
如果我们把一维数组作为元素,这时候就是二维数组,二维数组作为元素的话就是三维数组,二维数组及以上的数组统称为多维数组。我们一般用到二维数组就够了。
其本质就是“数组中的数组”。
6.2 二维数组的创建
我们该如何定义二维数组呢?
// 定义二维数组
#include <stdio.h>
int main()
{
int arr[3][5];
// int -- 类型 -- 表明数组内每个元素类型是整型
// arr -- 数组名 -- 这个数组的名字
// [3] -- 行 -- 表明这个数组有3行
// [5] -- 列 -- 表明每一行都有5个元素
return 0;
}
注意:方括号内的值为常量值。
7. 二维数组的初始化
我们知道了如何创建二维数组,只需要在创建的时候给定一些初始值就被称为初始化。
初始化二维数组是建立在初始化一维数组的基础上的。下面是二维数组的初始化形式:
// 二维数组的初始化
#include <stdio.h>
int main()
{
// 完全初始化
int arr1[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
// 不完全初始化
int arr2[3][5] = { 1,2 };
// 第一行前两个元素初始化为1 2,其它的元素为0
int arr3[3][5] = { 0 };
// 第一行第一个元素初始化为0,其它默认为0, 就是全部初始化为0的意思
// 按行初始化
int arr4[3][5] = { {1,2},{3,4},{5,6} };
// 把第一行、第二行、第三行前两个元素初始化为1 2 / 3 4 /5 6 其余为0
// 初始化时可以省略行, 但是不能省略列
int arr5[][5] = { 1,2,3 };
int arr6[][5] = { 1,2,3,4,5,6,7 };
int arr7[][5] = { {1,2},{3,4},{5,6} };
return 0;
}
在二维数组的初始化中,我们可以根据自己的目的来选择初始化的方法。
8. 二维数组的使用
8.1 二维数组的下标
二维数组的访问也是使用下标形式的,只不过是在一维数组的基础上加了列。所以,二维数组是有行和列的,只要锁定了行和列,则数组的元素就被锁定了。
C语言规定:二维数组的下标是从[0][0]开始的。
// 二维数组的下标
#include <stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
return 0;
}
在这里创建了一个三行五列的数组,我们来看看数组内部:
可以很明显的看出,数组的下标是从[0][0]开始的。所以,假设我们想输出7,则是arr[2][4],可以快速定位到。
8.2 二维数组的输入输出
与访问一维数组类似,我们同样使用循环。
// 二维数组的输入输出
#include <stdio.h>
int main()
{
int arr[3][5] = { 0 };
for (int i = 0; i < 3; i++) // 遍历行 产生行号
{
for (int j = 0; j < 5; j++) // 遍历列 产生列号
{
scanf("%d", &arr[i][j]);// 输入数据
}
}
for (int i = 0; i < 3; i++) // 产生行号
{
for (int j = 0; j < 5; j++) // 产生列号
{
printf("%d ", arr[i][j]);// 输出数据
}
printf("\n");
}
return 0;
}
// 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 //输入的数据
// 1 2 3 4 5 // 输出
// 2 3 4 5 6
// 3 4 5 6 7
不难看出,我们只需要知道行数和列数,就可以访问整个数组 。
9. 二维数组在内存中的储存
二维数组的储存方式也是连续存放的,我们先打印数组所有元素的地址:
// 打印二维数组各元素的地址
#include <stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return = 0;
}
其运行后:
每一行内部的每个元素都是相邻的,由于是整型,故而差4个字节。所以二维数组中的每个元素都是连续存放的。这也为我们指针访问数组元素奠定了基础。
10. C99中的变长数组(VLA)
前面我们有说过,数组的大小只能填常量值,如果填入变量值会发生什么?
编译器直接报了错误。很显然,这样的语法限制,极大程度的干扰了数组的灵活性,有时候数组大了浪费空间,数组小了容易越界。这让我们很困扰。于是乎:
C99新增了变长数组(variable-length array, VLA),允许使用变量表示数组的维度。如下所示:
int n = a + b;
int arr[n];
由此可见:变长数组有一些限制,变长数组必须是自动储存类别。即变长数组的根本特征是:数组的长度只有在运行时才能知道,所以变长数组不能初始化。
注意 变长数组不能改变大小
变长数组中的“变”不是指可以修改已创建数组的大小。一旦创建了变长数组,它的大小则保持不变。这里的“变”指的是:在创建数组时,可以使用变量指定数组的维度。
由于变长数组是C语言的新特性,目前完全支持这一特性的编译器不多。这里我所用的VS2022在默认情况下是不能使用的。就没办法给大家做演示了。