数组

4 阅读7分钟

  一、数组核心概念

数组是一组相同类型元素的集合,核心特征有二:一是元素个数≥1,二是所有元素类型一致。数组分为一维数组和多维数组(常用为二维数组),是 C 语言中存储批量同类型数据的基础结构。

二、一维数组

1. 创建与初始化

  • 创建语法type arr_name[常量值];,其中type为元素类型(如intchar等),arr_name为数组名,[]内常量值指定数组大小。示例:int math[20];(存储 20 个数学成绩)。

  • 初始化方式

    • 完全初始化:int arr[5] = {1,2,3,4,5};(所有元素明确赋值)。
    • 不完全初始化:int arr2[6] = {1};(仅首元素赋值,剩余默认初始化为 0)。
    • 错误示例:int arr3[3] = {1,2,3,4};(初始化项数量超过数组大小)。
  • 数组类型:去掉数组名后的部分即为数组类型,如int arr[10]的类型是int [10]

2. 使用方法

  • 下标访问:数组下标从 0 开始,最后一个元素下标为n-1n为元素个数),通过[](下标引用操作符)访问,示例:arr[3]访问下标为 3 的元素。

  • 遍历操作:通过for循环生成所有下标,实现数组元素的打印或输入。

    • 打印示例:循环i从0到n-1,输出arr[i]
int arr[]={1,2,3,4,5,6,7,8,9,10};  //0-9
	for(int i=0;i<10;i++){
		printf("%d ",arr[i]);
	}

    • 输入示例:循环i从0到n-1,通过scanf("%d", &arr[i])赋值。
	int arr[10]={};  
	int i=0;
	for( i=0;i<10;i++){
		scanf("%d ",&arr[i]);
		printf("%d ",arr[i]);
	}

3. 内存存储特性

数组元素在内存中连续存放,下标增长时地址由小到大递增,相邻元素地址差等于元素类型所占字节数(int数组相邻元素差 4 字节),为指针访问数组奠定基础。

	int arr1[10]={ 0 };
	char arr2[10]={0};
	printf("%zd\n",sizeof(arr1));//40
	printf("%zd\n",sizeof(int[5]));//20

	 //&p-专门打印地址 
	int arr[10]={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]); //&取地址,%p 十六进制 
//		&arr[0]= 0062fea0    两个之间差4
//		&arr[1]= 0062fea4    数组在内存中是连续存放的 
//		&arr[2]= 0062fea8    随着下标的增长,地址是由小到大变化的 
//		&arr[3]= 0062feac
//		&arr[4]= 0062feb0
//		&arr[5]= 0062feb4
//		&arr[6]= 0062feb8
//		&arr[7]= 0062febc
//		&arr[8]= 0062fec0
//		&arr[9]= 0062fec4
	}

4. 元素个数计算

使用sizeof关键字计算:数组元素个数 = sizeof(数组名) / sizeof(数组首个元素),示例:int sz = sizeof(arr)/sizeof(arr[0]);,该方法可灵活适配数组大小变化。

	int arr[10]={0};
	printf("%zd\n",sizeof(arr));//40
	printf("%zd\n",sizeof(arr[0]));//4
	printf("%zd\n",sizeof(arr)/sizeof(arr[0]));//计算元素个数 

三、二维数组

1. 概念与创建

  • 概念:以一维数组为元素的数组,本质是 “数组的数组”,常用于表示表格类数据。
  • 创建语法type arr_name[常量值1][常量值2];,其中常量值1表示行数,常量值2表示列数。示例:int arr[3][5];(3 行 5 列的整型二维数组)。

2. 初始化方式

  • 不完全初始化:int arr1[3][5] = {1,2};(按顺序赋值,未赋值元素默认 0)。
  • 完全初始化:int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};(依次填满所有元素)。
  • 按行初始化:int arr4[3][5] = {{1,2},{3,4},{5,6}};(每行元素用大括号分隔,未赋值元素默认 0)。
  • 省略行不省略列:int arr5[][5] = {1,2,3};(编译器可根据列数和元素个数自动计算行数)。

3. 使用方法

  • 下标访问:行下标和列下标均从 0 开始,通过arr[行号][列号]锁定元素,示例:arr[2][4]访问第 3 行第 5 列元素。
  • 遍历操作:嵌套for循环,外层循环控制行号,内层循环控制列号,实现输入或输出。
	int arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}} ;
	int i=0,j=0;
	for(i=0;i<3;i++){  //行 
		for(j=0;j<5;j++){  //列 
			scanf("%d ",&arr[i][j]);
			printf("%d ",arr[i][j]); 
		}
		printf("\n");
	}

4. 内存存储特性

二维数组的所有元素在内存中连续存放,不仅同一行内元素相邻(地址差为元素类型字节数),跨行元素(如arr[0][4]arr[1][0])也相邻,地址连续递增。

	//把数组每个元素地址打印出来 
	int arr[3][5]={0} ;
	int i=0,j=0;
	for(i=0;i<3;i++){  //行 
		for(j=0;j<5;j++){  //列 
			printf("&arr[%d][%d] =%p\n",i,j,&arr[i][j]); 
		}
	}
	//	&arr[0][0] =0062fe8c
	//	&arr[0][1] =0062fe90
	//	&arr[0][2] =0062fe94
	//	&arr[0][3] =0062fe98
	//	&arr[0][4] =0062fe9c
	//	&arr[1][0] =0062fea0
	//	&arr[1][1] =0062fea4
	//	&arr[1][2] =0062fea8
	//	&arr[1][3] =0062feac
	//	&arr[1][4] =0062feb0
	//	&arr[2][0] =0062feb4
	//	&arr[2][1] =0062feb8
	//	&arr[2][2] =0062febc
	//	&arr[2][3] =0062fec0
	//	&arr[2][4] =0062fec4

四、C99 变长数组(VLA)

  • 特性:C99 标准新增,允许使用变量指定数组大小,数组长度在程序运行时确定(非编译时),但大小确定后不可修改,且不能初始化。
  • 语法示例int n = 5; int arr[n];n为变量,数组大小由运行时输入或计算决定)。
  • 注意:VS2022 不支持该特性,GCC 等编译器可正常使用。
	//C99变长数组 
	int n = 0;
	scanf("%d", &n);//根据输入数值确定数组的大小
	int arr[n];
	int i = 0;
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
	}
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	

五、数组实战练习

1. 字符两端汇聚

通过left(左指针)和right(右指针)从数组两端向中间移动,逐步替换目标字符,结合Sleep函数实现动态演示效果。

	char arr1[] = "welcome to bit...";  //两个数组长度一样 
	char arr2[] = "#################";
	int left = 0;  
	int right = strlen(arr1)-1;
	printf("%s\n", arr2);
	while(left<=right)
	{
		Sleep(1000);  //需要<windows.h> 1000ms 
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		left++;  //left往后走 
		right--; //right往前走 
		printf("%s\n", arr2);
	}
//	#################
//	w###############.
//	we#############..
//	wel###########...
//	welc#########t...
//	welco#######it...
//	welcom#####bit...
//	welcome### bit...
//	welcome #o bit...
//	welcome to bit...

2. 二分查找(折半查找)

  • 适用场景:有序(升序 / 降序)数组中查找指定元素,效率远高于遍历。

  • 核心逻辑

    1. 定义left(左边界下标)、right(右边界下标,初始为数组最后一个元素下标)。
    2. 计算中间下标mid = left + (right - left)/2(避免left+right溢出)。
    3. 比较arr[mid]与目标值:大于目标值则调整右边界right = mid-1,小于则调整左边界left = mid+1,等于则找到目标。
    4. 循环终止条件:left > right(未找到)或找到目标元素。
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int left = 0;
	int right = sizeof(arr)/sizeof(arr[0])-1;
	int key = 7;//要找的数字
	int mid = 0;//记录中间元素的下标
	int find = 0;
	while(left<=right)
	{
			mid = (left+right)/2;
		if(arr[mid]>key)
		{
			right = mid-1;
		}
		else if(arr[mid] < key)
		{
			left = mid+1;
		}
		else
		{
			find = 1;
			break;
		}
	}
	if(1 == find )
		printf("找到了,下标是%d\n", mid);
	else
		printf("找不到\n");