C语言学习笔记---数组篇章

C语言数组

/知识点汇总/

1、一维数组的创建和初识化

概念
数组就是一组相同类型元素的集合,或者说表示类型相同的数据

数组的创建方式

type_t arr_name[const_n];
//type_t ---- 数组的元素类型
//arr_name ---- 数组名
//const_t --- 常量表达式,指定数组的大小

举例说明数组的创建

#include <stdio.h>
int main()
{
	int arr[5];
	int arr2[2 + 3];
	char arr3[8];
	//C99之前数组只能是常量来指定大小
	//C99之后引用了变长数组的概念,数组的大小就可以使用变量来指定了
	//但是C99标准下,数组的大小可以使用变量指定,但是数组不能初识化
	//但是Vs2022/Vs2019 不支持C99的变长数组
	int n = 0;
	scanf("%d",&n);
	int arr4[n];

	return 0;
}

2、数组的初识化

概念
数组的初识化是指,在创建数组的同时给数组的内容一些合理初始值(初识化)

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3 };//不完全初识化,剩余元素默认补0
	int arr2[4] = { 1,2,3,4 };//完全初始化

	char arr4[3] = { 'a',98,'c' };

	int arr3[] = {1,2,3};//省略数组大小, 由编译器自行判断数组内的元素
	
	char arr7[] = {'a','b','c'};//'/0'字符串结束标志
	char arr8[] = "abc";
	return 0;
}

3、一维数组的使用

[] —> 下标引用符

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n",arr[5]);//6
	//通过[]下标引用操作符,打印数组元素
	int i = 0;
	//for (i = 0; i < 10; i++)
	//{
	//	printf("%d\n",arr[i]);
	//}
	//计算数组元素个数
	int sz = sizeof(arr) / sizeof(arr[0]);
	//更改元素
	for (i = 0; i < sz; i++)
	{
		arr[i] = 10 - i;
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

3.1、一维数组在内存中的存储

观察存储情况

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//通过[]下标引用操作符,打印数组元素
	int i = 0;
	//计算数组元素个数
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %p\n",i,&arr[i]);
		//&arr[0] = 0019F908
		//&arr[1] = 0019F90C
		//&arr[2] = 0019F910
		//&arr[3] = 0019F914
		//&arr[4] = 0019F918
		//&arr[5] = 0019F91C
		//&arr[6] = 0019F920
		//&arr[7] = 0019F924
		//&arr[8] = 0019F928
		//&arr[9] = 0019F92C
		//观察结果可知,相邻元素之间相差4,单位字节
	}
	return 0;
}

小结:
(1)、数组在内存中是连续存放的
(2)、随着下标的增长,地址是由低到高变化的

4、二维数组的创建和初识化

[ ][ ] – 几行几列

#include <stdio.h>
int main()
{
	int arr[3][5];//整型二维数组创建
	char arr2[4][10];//字符型二维数组创建

	int arr3[2][3] = {0};
	//char arr4[3][5] = {1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};
	char arr4[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//大括号的作用约束,元素是否按照顺序放满之后再存放下一行
	int arr5[4][10] = { {1,2},{2,3,4},{5,5,5} };
	//注意:行可省略,列不可以省略
	//因为列确定一行所放的元素,行根据初识化几行就放几行
	int arr6[][3] = {1,2,3,4,5,6,7,8,9};//3行3列
	return 0;
}

小结
(1)、对于二维数组,如果初识化了,对于行数是可以省略的,但列是不能省略的
(2)、大括号的作用约束,元素是否按照顺序放满之后再存放下一行

4.1、二维数组的使用

二维数组的访问依然是通过下标进行访问

4.1.1、打印二维数组的元素例程
#include <stdio.h>
int main()
{
	int arr[3][5] = { {1,2},{4,5},{6,7,8} };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ",arr[i][j]); 
		}
		printf("\n");
	}
	return 0;
}
#include <stdio.h>
int main()
{
	int arr[3][5] = { {1,2},{4,5},{6,7,8} };
	int i = 0;
	//数组元素1~15
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			arr[i][j] = i * 5 + j + 1;
		}
	}
	//打印1~15数组元素
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

4.2、二维数组在内存中的存储

#include <stdio.h>
int main()
{
	int arr[3][5] = { {1,2},{4,5},{6,7,8} };
	int i = 0;
	//打印数组元素
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("&arr[%d][%d] = %p\n",i,j, &arr[i][j]);
			//&arr[0][0] = 00AFFC00
			//&arr[0][1] = 00AFFC04
			//&arr[0][2] = 00AFFC08
			//&arr[0][3] = 00AFFC0C
			//&arr[0][4] = 00AFFC10

			//&arr[1][0] = 00AFFC14
			//&arr[1][1] = 00AFFC18
			//&arr[1][2] = 00AFFC1C
			//&arr[1][3] = 00AFFC20
			//&arr[1][4] = 00AFFC24

			//&arr[2][0] = 00AFFC28
			//&arr[2][1] = 00AFFC2C
			//&arr[2][2] = 00AFFC30
			//&arr[2][3] = 00AFFC34
			//&arr[2][4] = 00AFFC38
			//通过结果可知,相邻的元素间隔是4,单位字节
			//连续存放的,第一行放完接着第二行放
			//行数可以省略,列数不可以省略
			//随下标的增长,地中也是由低到高的存放的
		}
		printf("\n");
	}
	return 0;
}

小结
(1)、通过结果可知,相邻的元素间隔是4,单位字节
(2)、连续存放的,第一行放完接着第二行放
(3)、二维数组可理解为一维数组的数组
(4)、行数可以省略,列数不可以省略
(5)、随下标的增长,地址也是由低到高的存放的
(6)、二维数组名:
如:arr[m][n] ---->数组名为arr[m]

4.3、数组越界

数组的下标规定是从0开始的,如果有n个元素,那么最后一个元素的下标就是n-1,
所以,当数组的下标小于0或大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
另外,C语言本身并不会进行越界检查,部分编译器也不一定会进行报错,容易导致程序出现难以发现的bug

4.3.1、数组越界例程
#include <stdio.h>
int main()
{
	int arr[10] = {1,2,3,4,5,6};//下标0~9
	int i = 0;
	for (i = 0; i <= 10; i++)//下标=0时,数组越界
	{
		printf("%d ",arr[i]);//数组越界
	}

	return 0;
}

4.4、数组作为函数的参数

数组可以作为函数的参数
经典的冒泡排序
核心思想:两两相邻的元素比较
冒泡排序的思路

第一趟:
10 9 8 7 6 5 4 3 2 1
9 10 8 7 6 5 4 3 2 1
9 8 10 7 6 5 4 3 2 1
......
9 8 7 6 5 4 3 2 1 10//此时完成一趟冒泡排序,将第一个元素排到了最后
第二趟:
8 9 7 6 5 4 3 2 1 10
8 7 9 6 5 4 3 2 1 10
.....
8 7 6 5 4 3 2 1 9 10//此时完成二趟冒泡排序,将第二个元素排到了最后
......
依次类推:
直至完成升序排序

数组作为函数的参数例程 — 冒泡排序

#include <stdio.h>
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
	//将arr数组进行排序,排序为升序
	//冒泡排序
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//趟数
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1-i; j++)// sz - 1-i元素比较的对数
		{
			if (arr[j] > arr[j + 1])//交换
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

数组作为函数的参数例程 — 函数写法 – 待优化

#include <stdio.h>

void sort(int arr[] ,int sz)
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)// sz - 1-i元素比较的对数
		{
			if (arr[j] > arr[j + 1])//交换
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
	//将arr数组进行排序,排序为升序
	//冒泡排序
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

小结
发现数组传参过去,代码执行结果与预想不同
探讨数组传参

4.4.1、数组名是什么?

数组名就是地址,通常来说数组名就是数组首元素的地址
但是有两个例外
(1)、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
(2)、& — 取地址操作符 —> &数组名,表示整个数组,取出的是整个数组的地址
除此之外,所遇到的数组名都是数组首元素地址

数组名例程

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };

	printf("%p\n",arr);
	printf("%p\n", &arr[0]);
	printf("%d\n", sizeof(arr));
	//对数组名进行加1的情况
	printf("%p\n", arr);
	printf("%p\n",arr+1);//+4

	printf("%p\n", &arr[0]);
	printf("%p\n",&arr[0]+1);//+4

	printf("%p\n",&arr);
	printf("%p\n",&arr+1);//+28(十六进制) --> +40(十进制),加的整个数组大小

	return 0;
}

小结
(1)、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
(2)、& — 取地址操作符 —> &数组名,表示整个数组,取出的是整个数组的地址
(3)、数组传参的本质就是传首元素的地址

探讨了数组名作为参数的知识,再回到冒泡排序
数组作为函数的参数例程 — 冒泡排序函数写法 — 优化

#include <stdio.h>
//首元素地址,本质就是指针接收参数int arr[] ----> int* arr即可等价
void sort(int* arr ,int sz)
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)// sz - 1-i元素比较的对数
		{
			if (arr[j] > arr[j + 1])//交换
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
	//将arr数组进行排序,排序为升序
	//冒泡排序
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr,sz);//学习了什么是数组名,可知这里的arr,不是特殊情况,就是数组的首元素地址
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

5、结语

半亩方糖一鉴开,天光云影共徘徊。
问渠哪得清如许?为有源头活水来。–朱熹(观书有感)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值