先赞后看,不足指正!
这将对我有很大的帮助!
所属专栏:C语言知识
阿哇旭的主页:Awas-Home page
引言
什么是数组?数组是C语言中重要的组成部分。从字面意思上来讲,数组就是一组相同元素类型元素的集合。在数组中可以存放一个或多个数据,但元数个数不能为零,且元素类型必须是相同的。而数组也分为一维数组和多维数组,我们常见的多维数组一般是二维数组。
那么,话不多说,我们一起来看看吧!
1. 一维数组
1.1 创建
一维数组的定义形式如下:
type arr_name[常量表达式];
- type 就是存放在数组中的数据的类型,例如int、char、float等,也可以自定义类型。
- arr_name 就是数组的名字,没有过多的要求,根据实际情况,简单易读即可。
- [ ] 中的常量数值用来指定数组的大小,即存放元素的个数,根据实际情况指定数组的大小即可。
- 补充:在对C99标准修订后,新增了可变长度数组:Variable-length array (VLA);即[ ]中的数值可以是未知数。
1.1.1 举例
int arr1[4];// 当前数组有4个元素,且元素类型为int型
char arr[5];// 字符型一维数组
float arr3[6];// 单精度浮点型一维数组
//...
int arr4[4 + 3];// 常数值也可以是一个表达式
1.2 初始化
在数组创建的时候,要给定初始值,该过程称为初始化。那么一维数组是如何初始化的?数组的初始化是使用大括号,将初始化值放在当中即可。而初始化也分为完全初始化和不完全初始化。
1.2.1 举例
int arr1[4] = { 1,2,3,4 };// 完全初始化
int arr2[6] = { 1,2 };// 不完全初始化 如图 1 所示
/* 初始化两个元素,剩余元素默认初始化为零
(在调试监视中可以观察到)*/
int arr3[4] = { 1,2,3,4,5 };// 错误示例
/* 初始化项大于数组元素项 */
int arr4[];// 错误示例
/* 未指定大小,也未初始化 */
图 1
图 2
在初始化时,可以不指定数组的大小,但要初始化数组,数组初始化几个元素,默认数组就有几个元素。还有就是不初始化数组,就要指定数组的大小,否则编译器就会报错。(如图 2 所示)
1.3 实际使用
1.3.1 一维数组下标
在C语言中,规定数组元素是有下标的,下标从0开始,当数组有n个元素时,最后一个元素的下标为n-1,就相当于每个元素都有对应的编号。 如下所示:
int arr[8] = { 1,2,3,4,5,6,7,8 };
数组元素与下标
1.3.2 一维数组的输入
在实际应用中,我们可能会将输入内容存放到数组当中,和正常的输入是一样的,只是把输入对象切换成为了数组。
在C语言中数组的访问提供了⼀个操作符 [ ] ,该操作符称为:下标引操作符。
int main()
{
int arr[10] = { 0 };
printf("为数组添加元素:>\n");
for (int i = 0; i < 10; i++)
{
scanf("%d", arr[i]);/*元素个数不要超过数组大小
否则会导致栈溢出*/
}
return 0;
}
1.3.3 一维数组的打印
输出也是一样的,利用循环语法,将输出对象切换为数组元素输出即可。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("打印数组元素:>\n");
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
1.4 一维数组在内存中的存储
1.4.1 数组名
在C语言中,我们知道,数组名表示数组首元素的地址。那么就有 arr == &arr[0],代码证明如下:
int main()
{
int arr[4] = { 1,2,3,4 };
if (arr == &arr[0])
{
printf("地址相同\n");
}
else
{
printf("地址不相同\n");
}
return 0;
}
1.4.2 数组元素的存储
在上面,我们知道了数组名表示数组首元素的地址后,我们又可以观察数组元素在内存中是怎么存储的。为此,我们需要把每个元素的地址给打印出来。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
for (int i = 0; i < 10; i++)
{
printf("&arr[%d]=%p\n", i, &arr[i]);
}
return 0;
}
数组元素在内存中是连续存放的
从上述结果中我们可以看出,数组随着下标的增长,地址是由小到大变化的,并且我们发现每两个相邻的元素之间相差4(因为一个整型是4个字节)。所以我们得出结论:数组在内存中是连续存放的。我们可以理解记忆该知识点,为指针的学习打下基础。
2. 二维数组
上文讲解的数组被称为一维数组,数组的元素都是内置类型的,如果我们把一维数组做为数组的元素,这时候就是二维数组,二维数组作为数组元素的数组被称为三维数组,二维数组以上的数组统称为多维数组。
2.1 创建
二维数组的定义形式如下:
type arr_name[常量表达式][常量表达式];
- 类比一维数组,只是二维数组的第一个常量表达式表示行,第二个常量表达式表示列。
2.1.1 举例
int arr1[4][4];// 当前数组有16个元素,且元素类型为int型
char arr2[5][5];// 字符型二维数组
float arr[6][6];// 精度浮点型二维数组
//...
2.2 初始化
二维数组的初始化与一维数组的初始化类似,也是使用大括号,将初始化值放在当中即可。
2.2.1 举例
int arr1[2][3] = { 1,2 };// 不完全初始化 如图 3 所示
/* 初始化两个元素,剩余元素默认初始化为零
(在调试监视中可以观察到)*/
int arr2[2][3] = { 1, 2, 3, 4, 5, 6 };// 完全初始化
int arr3[2][3] = { {1,2,3},{4,5,6} };// 按行初始化
int arr4[2][2] = { 1, 2, 3, 4, 5, 6 };// 错误示例
/* 初始化项大于数组元素项 */
int arr5[][];// 错误示例,如图 4 所示
int arr6[3][];
/* 初始化时省略⾏,但是不能省略列 */
图 3
图 4
2.3 实际使用
2.3.1 二维数组下标
二维数组访问也是使用下标的形式,二维数组是有行和列的,只要锁定了行和列就能唯一锁定数组中的一个元素。在C语言中,二维数组的行是从0开始的,列也是从0开始的。如下图所示:
int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
数组元素与下标
2.3.2 二维数组的输入
思路与一维数组相同,通过循环实现生成所有的下标并初始化数组。
int main()
{
int i = 0, j = 0;
int arr1[2][3] = { 0 };
printf("为数组添加元素:>\n");
for (i = 0; i < 2; i++)// 行
{
for (j = 0; j < 3; j++)// 列
{
scanf("%d", arr1[i][j]);
}
}
return 0;
}
2.3.3 二维数组的打印
输出与一维数组一样,利用循环语法,将输出对象切换为数组元素输出即可。
int main()
{
int i = 0, j = 0;
int arr1[2][3] = { 1,2,3, 4,5,6 };
printf("打印数组元素:>\n");
for (i = 0; i < 2; i++)// 行
{
for (j = 0; j < 3; j++)// 列
{
printf("%d ", arr1[i][j]);
}
printf("\n");
}
return 0;
}
2.4 二维数组在内存中的存储
2.4.1 数组名
二维数组的数组名也是一个地址,但与一维数组又不相同,而二维数组的数组名表示的是第一行的地址。但我们可能会有疑问,为什么二维数组的数组名表示的是第一行的地址呢?
int main()
{
int arr[4][4] = { 0 };
printf("%p %p\n", arr, &arr[0][0]);
return 0;
}
在C语言中,二维数组在内存中是以连续的一维数组的形式存储的。当我们声明一个二维数组时,实际上是在内存中分配了一块连续的内存空间来存储数组的元素。
对于一个二维数组,可以将其视为一个由多个一维数组组成的数组。例如,一个3行4列的二维数组可以看作是由3个长度为4的一维数组组成的。当我们使用数组名来表示一个二维数组时,它实际上表示的是数组的首元素的地址,也就是第一行的地址。所以,找到首元素的地址,就能找到该行所有元素的地址。
2.4.2 数组元素的存储
像一维数组一样,我们如果想研究二维数组在内存中的存储方式,我们可以打印出数组中所有元素的地址。
int main()
{
int arr[2][3] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("&arr[%d][%d] = %p ", i, j, &arr[i][j]);
}
printf("\n");
}
return 0;
}
二维数组中的每个元素都是连续存放
观察上面每个元素的地址,每一行内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置处的两个元素(如:arr[0][2]和arr[1][0])之间也是差4个字节,所以二维数组中的每个元素都是连续存放的。
3. 变长数组
3.1 介绍
在C99标准之前,C语言在创建数组的时候,数组大小的指定只能使用常量、常量表达式,或者如果我们初始化数据的话,可以省略数组大小。
int arr1[10];
int arr2[4 + 5];
int arr3[] = { 1,2 };
- 这样的语法限制,让我们创建数组就不够灵活,有时候数组大了浪费空间,有时候数组小了不够用的。
- C99中给一个变长数组(variable-length array,简称 VLA)的新特性,允许我们可以使用变量指定数组大小。
- 变长数组中的“变”不是指可以修改已创建数组的大小,一旦创建了变长数组,它的大小则保持不变。这里的“变”指的是:在创建数组时,可以使用变量指定数组的长度。(普通数组只能用常量或常量表达式指定数组的长度),需在支持C99标准的编译器上才行(注意,VS系列编译器均不支持该特性)。
3.2 应用
int main()
{
int sz;
scanf("%d", sz);
int arr[sz];
}
- 变长数组的根本特征,就是数组长度只有运行时才能确定,所以变长数组不能初始化。
4. 练习
有序列表的折半查找,源码如下:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//定义整形数组并初始化
int key;//要查找的数字
printf("请输入要查找的数字:\n");
scanf("%d", &key);
int size = sizeof(arr) / sizeof(arr[0]);//数组arr[]的元素个数
int flag = 0;//假设查找到目标元素
int left = 0;//数组arr[]最左边元素下标
int right = size - 1; //数组arr[]最右边元素下标
while (left <= right)
{
int mid = (left + right) / 2;//中间元素下标
if (arr[mid] > key)//判断中间元素值大于目标元素值
{
right = mid - 1;//去除中间元素及其右边的元素,并将去除前中间元素下标减一赋值给去除前中间元素的前一个元素
}
else if (arr[mid] < key)//判断中间元素值小于于目标元素值
{
left = mid + 1;//去除中间元素及其左边的元素,并将去除前中间元素下标加一赋值给去除前中间元素的后一个元素
}
else
{
flag = 1;//查找到目标元素
printf("查找到目标元素,下标为%d\n", mid);
break;//跳出循环
}
}
if (flag == 0)
{
printf("未查找到目标元素\n");
}
return 0;
}
求中间元素的下标,使用 mid = (left+right)/2 ,如果 left 和 right 比较大的时候可能存在问题,可以这样解决:
mid = left+(right-left)/2;
5. 结语
希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。
完结!咻~