文章目录
一、数组的概念
数组是⼀组相同类型元素的集合,一般存放的是一个或者多个数据。数组分为一维数组与多维数组,多维数组使用较多的一般是二维数组。
二、⼀维数组的创建和初始化
1、数组的创建
⼀维数组的语法:type arr_name[常量值]
存放在数组内的值被称为数组的元素,数组在创建的时候可以指定数组的⼤⼩和数组的元素类型。
①:type 是数组中存放数据的类型,如 char、short、int、float 等或者⾃定义的类型。
②: arr_name 是数组名,起的有意义即可。
③:[ ] 中的常量值⽤来指定数组⼤⼩。
例如:我们想存储某个班级20⼈的数学成绩,那我们就可以创建⼀个数组:int math[20]
同样也可以根据需要创建其他类型大小的数组,如下:
char ch[8];
double score[10];
2、数组的初始化
数组在创建的时候,有时需要进行初始化,那么数组是怎样初始化的呢?实际上数组的初始化就是将数据放在大括号中。
初始化一般分为完全初始化和不完全初始化,像int arr1[5] = { 1,2,3,4,5 }
就是完全初始化,元素个数等同于数组大小;而int arr2[5] = { 1 }
就是不完全初始化,元素个数小于数组大小。
3、数组的类型
数组也是有类型的,一般去掉数组名后剩下的就是数组的类型。例如int arr1[10]
的类型就是int [10]
三、⼀维数组的使⽤
学会了⼀维数组的基本语法后,我们知道⼀维数组是可以存放数据的,而存放数据的⽬的是对数据进行操作,那我们如何使⽤⼀维数组呢?
1、数组的下标
C语⾔规定数组是有下标的,下标从0开始,假设数组有n个元素,那么最后一个下标就是n-1,而每一个下标都依次对应一个元素。
例如:数组int arr[10] = {1,2,3,4,5,6,7,8,9,10}
的下标就是0到9,依次对应10个元素。

在C语⾔中对于数组的访问还提供了⼀个下标引⽤操作符:[ ]
如果想访问一个元素,那么就可以对其下标使用[ ]从而进行访问。
例如:想打印数组元素4和8。
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", arr[3]);
printf("%d\n", arr[7]);
return 0;
}
运行结果:
可以看到,如果想要访问元素4和8,那么我们只需要针对其下标3和7进行访问即可。
2、数组的输出
那么如果想要访问整个数组的内容,只需要产⽣数组所有元素的下标,再使⽤下标进行访问就⾏了。
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
3、数组的输⼊
如果我们想在数组输入自己想要的数据该怎么办呢?实际上和上面是一样的,先产生下标,再对下标对应的元素输入数据。
#include<stdio.h>
int main()
{
int arr[5] = { 0 };
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
for (int i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
可以看到数组本来为空,在我们输入5个数据后,数组存放的也就是我们输入的数据了。
四、⼀维数组在内存中的存储
如果想要深⼊了解数组,那我们就要了解数组在内存中的存储。
我们可以在x86环境下打印5个数组元素的地址来进行观察:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("&arr[%d]=%p\n", i, &arr[i]);
}
return 0;
}
运行结果:
我们可以看到,数组元素随着下标的增大,地址也是逐渐变大的,而且每一次都增长4个字节(一个整型大小)。所以可以得出结论:数组在内存中是连续存放的
五、sizeof计算数组元素个数
我们知道sizeof关键字是可以计算类型或者变量⼤⼩的,其实 sizeof 也可以计算数组的⼤⼩,单位是字节。
例如:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr));
return 0;
}
运行结果:
这⾥我们计算的是整个数组的大小,总共40个字节。
我们⼜知道数组中所有元素的类型都是相同的,那么只要计算出⼀个元素的大小,再用数组总大小除一个元素的大小,即:sizeof(arr) / sizeof(arr[0])
,最后得到的就是数组的元素个数了。
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int len = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", len);
return 0;
}
运行结果:
六、⼆维数组的创建和初始化
1、⼆维数组的概念

2、⼆维数组的创建
二维数组的语法:type arr_name[常量值1][常量值2]
注:第一个常量值表示行数,第二个常量值表示列数
3、⼆维数组的初始化
二维数组与一维数组的初始方法一致,分为了完全初始化,不完全初始化和按行初始化。
①完全初始化:int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 }
。
表示二维数组有三行,每一行有五个元素。
②不完全初始化:int arr[3][5] = {1,2}
。
表示二维数组有三行,每一行有五个元素。
③按行初始化:int arr[3][5] = { {1,2},{3,4},{5,6} }
。
我们在初始化的时候还要注意一点:初始化可以省略行,但是不能省略列。
例如: int arr[][5] = {1,2,3,4,5,6,7}
可以看到只要知道一行有几个元素,数据就会依次去占位,第一行满了就会进入下一行。
七、⼆维数组的使⽤
1、⼆维数组的下标
C语⾔规定,⼆维数组的⾏是从0开始的,列也是从0开始的,因此只要知道了行列的下标就可以定位出元素。例如int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 }
我们想要查找元素7,就可以分别找到其行列下标,在图中可以看到行下标为1,列下标位1。此时再使用下标引用操作符即可访问:
#include<stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 };
printf("%d", arr[1][1]);
return 0;
}
运行结果:
2、⼆维数组的输出
那如果想访问整个⼆维数组该怎么办呢?我们需要先产生行的下标,再在行的下标后面产生列的下标,再一一输出即可。
注:对于行的长度可以通过sizeof(arr) / sizeof(arr[0])
来计算,对于列的长度可以通过sizeof(arr[0]) / sizeof(arr[0][0])
来计算。
例如输出数组:int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 }
#include<stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 };
int rows = sizeof(arr) / sizeof(arr[0]);
int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果:
3、⼆维数组的输入
那么我们该怎么为二维数组输入数据呢?其实和二维数组输出数据一样,先产生行的下标,再在行的下标后面产生列的下标,再一一输入即可。
int main()
{
int arr[3][5] = { 0 };
int rows = sizeof(arr) / sizeof(arr[0]);
int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
printf("%d ", arr[i][j]);
}
}
printf("\n");
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
scanf("%d", &arr[i][j]);
}
}
printf("\n");
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
printf("%d ", arr[i][j]);
}
}
return 0;
}
运行结果:
可以看到二维数组本来没有数据,当我们输入之后,存放的就是输入的数据了。
八、⼆维数组在内存中的存储
像⼀维数组⼀样,我们想要深入了解二维数组,就需要了解⼆维数组在内存中的存储⽅式。
在x86环境下打印二维数组元素的地址:
#include<stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15 };
int rows = sizeof(arr) / sizeof(arr[0]);
int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
for (int i = 0; i < rows; i++)
{
for (int j= 0; j < cols; j++)
{
printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
}
}
return 0;
}
运行结果:
我们可以观察到,每⼀⾏元素地址依次递增4个字节,每一行地址也是依次递增4个字节,所以得出结论:⼆维数组中的每个元素也都是连续存放的。
如图所⽰:
九、C99中的变⻓数组
在C99标准之前,数组⼤⼩只能指定为常量或者常量表达式。如int arr1[10]
或者int arr2[3+5]
。如果初始化时可以省略数组大小。
有时候数组⼤了会浪费空间,数组⼩了会不够用,存在着限制。
因此C99标准中出现了变⻓数组,允许我们将数组大小指定为变量。例如:int arr[n]
。坏处是变⻓数组不能进行初始化,好处是程序在运⾏时可以根据变量的⼤⼩来指定数组的元素个数。
变长数组的使用:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int arr[n];
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
可惜在vs2022上不支持这一种用法,因此无法运行。
十、数组练习
练习1:多个字符从两端移动,向中间汇聚
思路:双变量指向两个数组的首尾下标,while循环依次对两个数组的首尾分别进行赋值。
#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
char arr1[] = { "welcome to world!" };
char arr2[] = { "*****************" };
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
Sleep(1000);
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}
运行结果:
练习2:⼆分查找
常规查找一个数,需要我们依次遍历每一个数,如果数的规模很大,效率就非常的低。而我们可以每次先查找中间的数,如果大了就查找另外一半边元素,小了同理,这样我们每次就可以省略掉一半需要查找的数据,效率直接大大提高了,这就叫做二分查找法!
思路:创建双变量指向数组首尾下标,再创建变量mid指向中间元素下标,while循环拿中间元素比较要查找的数,大了mid-1变成新的尾下标,小了mid+1变成新的首下标,直到找到要查找的数。
#include<stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;
int mid = 0;
int key ;
printf("请输入你要查找的数:");
scanf("%d", &key);
int find = 0;//先假设没有找到
while (left <= right)
{
mid = left + (right - left) / 2;
if (arr[mid] > key)
{
right = mid - 1;
}
else if (arr[mid] < key)
{
left = mid + 1;
}
else
{
//找到了 跳出循环
find = 1;
break;
}
}
if (find == 1)
{
printf("找到了,下标是%d\n",mid);
}
else
{
printf("没有找到\n");
}
return 0;
}
运行结果:
如果left和right都⽐较⼤的时候使用mid = (left+right)/2
,他们的和可能会超出数据的最大范围,因此使用mid = left+(right-left)/2
更加的安全。