「探索C语言数组:从基础到实际应用」

先赞后看,不足指正!

这将对我有很大的帮助!

所属专栏: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. 结语

        希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。

        完结!咻~

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值