【C语言从不挂科到高绩点】13-二维数组以及数组元素增加和删除

Hello!彦祖们,俺又回来了!!!,继续给大家分享 《C语言从不挂科到高绩点》课程!! 本节课重点讲解二维数组,以及如何在数组中添加和删除元素

本套课程将会从0基础讲解C语言核心技术,适合人群:

  1. 大学中开设了C语言课程的同学
  2. 想要专升本或者考研的同学
  3. 想要考计算机等级证书的同学
  4. 想要从事C/C++/嵌入式开发的同学

================点个关注吧================

=========================================

6.5 二维数组

6.5.1 二维数组的定义和使用

所谓二维数组,就是数组的数组,也就是数组中的元素还是一个数组

二维数组的定义方式

数据类型 数组名 [ 外层数组长度 ][ 内层数组的长度 ]

可以将二维数组看成一个 Excel 表格,外层数组的长度表示总行数,内层数组的长度表示每一行的列数。

int a[3][4];

类似定义了一个 3 行 4 列的表格,一共有 3*4=12 个单元格。对于二维数组来说,表示二维数组中有 3 个子数组,每个子数组中 4 个元素,一共 12 个元素。每个元素占 4 个字节。那么二维数组一共占:48 个字节。将这些内存统一取了个名字叫 a。

int a [3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
};

【参考代码】

#include <stdio.h>

int main(){

	int ary [3][4] = {
	    {1,2,3,4},
	    {5,6,7,8},
	    {9,10,11,12}
	};

	// 从二维数组中取出数据:数组名[外层数组的下标][内层数组的下标]
	// 外层数组的下标:要取出的数据当前所在的数组,在外层二维数组中的下标
	// 内层数组的下标:要取出的数据在当前数组中的下标

	// 取出2这个元素
	// 第1步:ary[0] 可以将数组中的第一个元素{1,2,3,4}数据取出来,还是一个数组,
	//        所以ary[0]整体可以看成{1,2,3,4}这个数组的数组名
	// 第2步:从数组名为ary[0]的数组中根据下标1取出数据ary[0][1]
	printf("%d\n",ary[0][1]);
	printf("%d\n",ary[1][3]);
	return 0;
}

需要注意:二维数组,本质上还是可以看成一维数组。这个一维数组中的数据不是一个具体的值而是一个数组。(二维数组就是数组的数组)

6.5.2 二维数组的遍历

C语言中没有严格意义的二维数组,

二维数组,本质上还是可以看成一维数组。这个一维数组中的数据不是一个具体的值而是一个数组

【参考代码】

#include <stdio.h>

int main(){

	int ary [3][4] = {
	    {1,2,3,4},
	    {5,6,7,8},
	    {9,10,11,12}
	};

	// 从二维数组中取出数据:数组名[外层数组的下标][内层数组的下标]
	// 外层数组的下标:要取出的数据当前所在的数组,在外层二维数组中的下标
	// 内层数组的下标:要取出的数据在当前数组中的下标

	// 取出2这个元素
	// 第1步:ary[0] 可以将数组中的第一个元素{1,2,3,4}数据取出来,还是一个数组,
	//        所以ary[0]整体可以看成{1,2,3,4}这个数组的数组名
	// 第2步:从数组名为ary[0]的数组中根据下标1取出数据ary[0][1]
	printf("%d\n",ary[0][1]);
	printf("%d\n",ary[1][3]);

	// 计算二维数组的长度
	//int len = sizeof(ary)/sizeof(int); // 获取到的是二维数组数据总数量
	int len = sizeof(ary)/sizeof(ary[0]);

	printf("%d\n",len );

	for (int i = 0; i < len; ++i)
	{
		// 获取到数组中的每一个元素 ary[i]
		// ary是一个二维数组,ary[i]取出来的还是一个数组
		// ary[i]看成数组名,现在需要遍历的数组就是ary[i]
		// i=0; ary[0]取出来的就是{1,2,3,4}这个数组,ary[0]可以看成这个数组的名字
		// i=1; ary[1]取出来的就是{5,6,7,8} ,ary[1]可以看成这个数组的名字
		int len1 = sizeof(ary[i])/sizeof(ary[i][0]);
		for (int j = 0; j < len1; ++j)
		{
			printf("%d\t",ary[i][j]);
		}
		printf("\n");

	}
	return 0;
}

6.5.3 二维数组的初始化

二维数组在概念上是二维的,但是在内存中并不存在二维数组,二维数组实际的硬件存储连续编址的,也就是内存中只有一维数组。放完一个一维数组之后,顺次放第二个。和一维数组的方式一样。

	int ary [3][3] = {
	    {1,2,3},
	    {5,6,7},
	    {9,10,11}
	};

它在内存中存放的方式:

需要注意的问题:二维数组整体占用的空间也是连续的

#include <stdio.h>


int main(){
	// 第一种初始化方式
	int ary [3][3] = {
	    {1,2,3},
	    {5,6,7},
	    {9,10,11}
	};


	// 二维数组在内存中,还是一个一维数组
	// 所以我们可以按照一维数组的方式初始化:
	int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};


	// 可以只给部分元素赋值:不足的位置,会补上0
	// int arr[3][4] = {1,2,3,4};

	// 将所有的成员值都设置为0
	//int arr[3][4] = {0};

	// 可以根据里层数组元素的个数,自动划分二维数组
	// int arr[][3] = {1,2,3,4,5,6,7,8};
	// 等价于:{{1,2,3},{4,5,6},{7,8,0}}


	// int arr[][] = {1,2,3,4,5,6,7,8};// 编译出错
	//int arr[3][] = {1,2,3,4,5,6,7,8}; // 编译出错

	// 定义二维数组最里层的数组长度需要指定



	int len = sizeof(arr)/sizeof(arr[0]);

	printf("%d\n",len );

	for (int i = 0; i < len; ++i)
	{
		// 获取到数组中的每一个元素 ary[i]
		// ary是一个二维数组,ary[i]取出来的还是一个数组
		// ary[i]看成数组名,现在需要遍历的数组就是ary[i]
		// i=0; ary[0]取出来的就是{1,2,3,4}这个数组,ary[0]可以看成这个数组的名字
		// i=1; ary[1]取出来的就是{5,6,7,8} ,ary[1]可以看成这个数组的名字
		int len1 = sizeof(arr[i])/sizeof(arr[i][0]);
		for (int j = 0; j < len1; ++j)
		{
			printf("%d\t",arr[i][j]);
		}
		printf("\n");

	}
	return 0;
}

6.5 C 语言中数组是静态的

在 C 语言中,数组一旦定义之后,占用的内存是固定的

int a[4] = {1,2,3,4};

这个数组 a 一旦定义好了之后,数组在内存中的占用的空间内存是固定。4*4=16 个字节。

数组的容量是不可以改变的。既不能在里面新增一个元素,也不能在任意位置上删除元素

只能读取和修改,这样的数组称为静态数组。

但是实际开发中,很多情况下必须对数组进行插入或者删除元素。解决这个问题的方案有两种:

  1. 造一个新的数组,用来存放增加或者删除后的数据
  2. 定义数组的时候,数组的长度设置一个较大的值。

6.5.1 删除数组中的元素

实现步骤:

  1. 新建一个数组,数组的长度比原数组要小
  2. 将原数组的数据,除了要删除的元素以外,逐个添加到新的数组中
  3. 最终得到的新数组,就是相对于原来数组而言,就是删除数据之后的数组。

【参考代码】

/**

	删除数组中指定下标的的元素

	1. 新建一个数组,数组的长度比原数组要小
	2. 将原数组的数据,除了要删除的元素以外,逐个添加到新的数组中
	3. 最终得到的新数组,就是相对于原来数组而言,就是删除数据之后的数组。
**/

#include <stdio.h>


int main(){

	// 原数组
	// 下标:       0 1 2 3 4 5 6 7 8 9
	int nums [] = {1,2,3,4,5,6,7,8,9,10};

	// 删除下标为6的元素
	// 删除之后下标:     0 1 2 3 4 5 6 7 8
	int nums_new[9]; //{1,2,3,4,5,6,8,9,10}

	//将原数组的数据,除了要删除的元素以外,逐个添加到新的数组中
	for (int i = 0; i < 10; ++i)
	{
		// 下标小于删除下标时,数据是原封不动的放入到新数组中
		// 此时数据在原数组中的下标和在新数组中的下标是一样的
		if(i<6){
			nums_new[i] = nums[i];
		}

		// 将下标为6的数据逐个搬运大新的数组中
		if(i>6){

			//因为删除了一个数据,数据在新数组中的下标,会比原数组中的下标小1
            //删除下标为6的元素之后,下标大于6的元素,都需要前移
            // 在新数组中的下标位置就相对于原数组来说是前移了的,所以要-1
			nums_new[i-1] = nums[i];
		}

	}

	// 遍历新数组,查看数据
	for (int i = 0; i < 9; ++i)
	{
		printf("%d\n", nums_new[i]);
	}

	return 0;
}

【练习】控制台输入一个数据,如果这个数据存在就将这个数据删除,如果不存在,提示用户数据不存在,删除失败。

【参考代码】

/**
控制台输入一个数据,
如果这个数据存在就将这个数据删除,
如果不存在,提示用户数据不存在,删除失败。

**/

#include <stdio.h>
int main(){

	int nums [] = {1,2,3,4,5,6,7,8,9,10};

	int nums_new[9]; 

	printf("请输入需要删除的数据:\n");
	int del = 0;
	scanf("%d",&del);

	// 遍历查找需要删除数据的下标
	int flag = -1; // 找不到数据,flag为-1,找到了数据flag就为找到数据的下标

	for (int i = 0; i < 10; ++i)
	{
		if (nums[i]==del)
		{
			flag = i;
			break;
		}
	}

	if(flag==-1){
		printf("数据不存在,删除失败\n");
	}else{ // 如果找到数据
		// 就根据下标删除数据,flag中当前存放的就是需要删除数据的下标
		for (int i = 0; i < 10; ++i)
		{
			// 下标小于删除下标时,数据是原封不动的放入到新数组中
			// 此时数据在原数组中的下标和在新数组中的下标是一样的
			if(i<flag){
				nums_new[i] = nums[i];
			}
			if(i>flag){

				//因为删除了一个数据,数据在新数组中的下标,会比原数组中的下标小1
				nums_new[i-1] = nums[i];
			}
		}

		printf("当前数组中的元素:");
		for (int i = 0; i <9; ++i)
		{
			printf("%d\t", nums_new[i]);
		}
		printf("\n");
	}


	return 0;
}

6.5.2 插入元素

实现步骤:

  1. 新建一个数组,数组的长度要比原数组要长
  2. 将原数组指定下标上的元素以及之前的的元素逐个复制到新的数组中
  3. 在新数组中当前位置掺入需要插入的元素
  4. 将原数组指定下标后的元素,再逐个复制到新数组中

【参考代码】

/**


1. 新建一个数组,数组的长度要比原数组要长
2. 将原数组指定下标上的元素以及之前的的元素逐个复制到新的数组中
3. 在新数组中当前位置掺入需要插入的元素
4. 将原数组指定下标后的元素,再逐个复制到新数组中

**/

#include <stdio.h>

int main(){

	// 原数组
	// 下标:       0 1 2 3 4 5 6 7 8 9
	int nums [] = {1,2,3,4,5,6,7,8,9,10};

	//新建一个数组,数组的长度要比原数组要长
	// 在下标为7的元素后面插入20
	//  0 1 2 3 4 5 6 7 8  9 10
	// {1,2,3,4,5,6,7,8,20,9,10}
	int nums_new[11];

	for (int i = 0; i < 10; ++i)
	{
		//将原数组指定下标上的元素以及之前的的元素逐个复制到新的数组中
		if(i<7){
			// {1,2,3,4,5,6,7,0,0,0,0}
			nums_new[i]= nums[i];
		}else if(i==7){
			//i = 7
		//下标:0 1 2 3 4 5 6 7 8 9 10
			//{1,2,3,4,5,6,7,8,0,0,0}
			nums_new[i] = nums[i];
			//{1,2,3,4,5,6,7,8,20,0,0}
			nums_new[i+1] = 20;
		}else{
			// i>7 的时候;
			// 由于在中间插入了一个数据,所以后面的数据相对于原数组而言
			// 位置要向后移动一位, 所以i要+1
			nums_new[i+1] = nums[i];
		}
	}

	for (int i = 0; i < 11; ++i)
	{
		printf("%d\t", nums_new[i]);
	}
	printf("\n");
	return 0;
}

6.5.3 添加和删除元素的另一种方式【重点掌握】

实现思路:

  1. 定义一个较长的数组:ary
  2. 手动定义长度变量 len:用这个长度变量代表 ary 数组中实际存放元素的个数
  3. 向数组中追加元素的时候,在 len 的位置上修改数据的值,ary[len]=值,将实际长度 len 加 1
  4. 删除元素时,将要删除的位置与 len-1 位置上的元素进行交换,然后将 ary[len-1]=0;数组的实际长度 len-1
  5. 后续在使用数组的时候,数组的长度始终使用 len,而不是使用 ary 的数组长度。

添加元素的原理图

【参考代码】

/**
	数组追加元素
*/

#include <stdio.h>

int main(){
	// {0,0,0,0,0,0,0,0,0,0}
	int nums[10]={0};

	int len = 0; // 实际元素的个数

	while(1){
		printf("请输入数据:");
		int num = 0;
		scanf("%d",&num);

		if(len==10){
			printf("数组已经满了,不能再添加数据了\n");
			break;
		}
		// 数组没有加满,就在len的位置上增加数据
		// 第1次循环 输入2  len = 0  nums[len]=nums[0]=2  len++ len=1
		// 			修改之后:nums={2,0,0,0,0,0,0,0,0,0}
		// 第2次循环 输入5  len =1   nums[len]=nums[1]=5  len++ len=2
		// 			修改之后:nums={2,5,0,0,0,0,0,0,0,0}
		// 第3次循环 输入8  len =2   nums[len]=nums[2]=8  len++ len=3
		// 			修改之后:nums={2,5,8,0,0,0,0,0,0,0}
		nums[len] = num;
		// 让len向后移动,方便下一次插入数据
		len ++;

		printf("当前数组中的数据:");
		// 遍历数组的时候,需要数组的长度时,就使用len
		for (int i = 0; i < len; ++i)
		{
			printf("%d\t",nums[i]);
		}
		printf("\n");
	}



	return 0;
}

删除数据

方式一:将要删除的数据和最后一个数据交换,然后 len-1

【参考代码】

/**
	删除数组元素

**/

#include <stdio.h>

int main(){

	int nums [] = {3,5,6,8,2,1,9,7};

	// 获取数组长度
	int len = sizeof(nums)/sizeof(int);

	while(1){
		printf("输入需要删除的数据:");
		int num = 0;
		scanf("%d",&num);

		//检查删除的数据是否存在于数组中
		int flag = -1;  // 如果找到了flag就为找到元素下标,找不到flag=-1
		for (int i = 0; i < len; ++i)
		{
			if(nums[i]==num){
				flag = i;
				break;
			}
		}

		if(flag==-1){
			printf("数组中不存在要删除的数据\n");
		}else{
			// 删除元素
			// 1. 将需要删除元素和最后位置上的元素交换位置
			//int temp = nums[flag];
			// 直接那最后一个位置上的元素,替换掉要删除的元素
			nums[flag] = nums[len-1];
			nums[len-1] = 0 ;// 最后一个位置上的元素赋值为0
			// 数组的长度减1
			len --;
		}

		if(len==0){
			printf("数组已经空了,不能删除数据了\n");
			break;
		}

		printf("当前数组中的元素:");
		for (int i = 0; i < len; ++i)
		{
			printf("%d\t",nums[i] );
		}
		printf("\n");



	}
	return 0;
}

方式二:找到要删除的位置,将后面的所有元素都向前移动一位,最后 len -1 【作业】

【参考代码】

// 将数组中的元素,从下标为flag的位置,后面的元素逐个向前移动
//  下标     0 1  2 3 4 5
// 假设数组:{8,9,10,7,6,2} 现在删除10 10的下标为2
for(int i=flag;i<len;i++){
    // 第1次循环  i=2  data[2]=data[3] 
    //  下标            0 1 2 3 4 5
    //  处理之后的数组:{8,9,7,7,6,2}

    // 第2次循环 i=3   data[3]=data[4]
    //  下标            0 1 2 3 4 5
    //  处理之后的数组:{8,9,7,6,6,2}

    // 第3次循环 i=4   data[4]=data[5]
    //  下标            0 1 2 3 4 5
    //  处理之后的数组:{8,9,7,6,2,2}

    // 第4次循环 i=5   data[5]=data[6] 
    //  下标            0 1 2 3 4 5
    //  处理之后的数组:{8,9,7,6,2,0}
    data[i] = data[i+1];

}

// 数组实际长度-1
len --;

6.6 数组的溢出和越界

6.6.1 数组的越界

C 语言中数组是静态的,不能自动扩容,当下标小于 0 或者大于等于数组的长度的时候,就会发生下标越界。所谓的下标越界实际上就是使用了数组中不存在的下标。

【案例代码】

/**

数组下标越界

**/

#include <stdio.h>

int main(){
	//            0   1  2
	int ary[3] = {10,20,30};

	printf("%d\n",ary[2] );

	printf("%d\n",ary[4] );  // ary中不存在下标为4的元素

	printf("%d\n",ary[-2]); // ary中不存在下标为-1的元素

	return 0;
}

运行效果:

需要注意的问题:

  1. C 语言为了提高效率,保证操作的灵活性,并不会对越界行为进行检查,哪怕下标越界了,编译器也不会报编译错误。只有在运行时可能会发生问题。
  2. 如果在操作数组过程中,数据显示特别大或者特别小,那就说明操作的下标越界了。
  3. 越界访问的数组元素的值是不确定的,没有实际含义,因为数组之外的内存我们并不知道存的是什么东西,可能是其他变量的值,也可能是函数的参数,也可能是一个地址。
  4. 开发中,一定要避免数组越界的问题。

6.6.2 数组的溢出

当赋予数组元素的个数,超出了数组定义的长度时,就会发生数组溢出。

int a[3] = {1,2,3,4,5};

数组的长度为 3,初始化的时候,给了 5 个元素,超出了数组的长度,此时就会发生数组的溢出。

对于数组的溢出而言,编译器是会检查的,如果发生溢出,编译就会出错。

 ----------------------------------------------------------------------

分享不易,耗时耗力,喜欢的同学给个关注和赞吧

承接毕设指导,技术答疑,学习路上想要找私人教练的同学可以私信我

更多学习资料,公众号:墨轩学习网,B站:墨轩大楼

----------------------------------------------------------------------

另有下图需求的也记得私信我哟,专业班子


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听潮阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>