目录
8.1 数组概念的引入
8.2.⼀维数组的创建和初始化
8.2.1 数组的创建
8.2.2数组的初始化
8.2.3 数组的类型
8.3 ⼀维数组的使⽤
8.3.1 数组下标
8.3.2 打印数组元素
8.3.3 输入数组元素
8.4 ⼀维数组在内存中的存储
8.5 sizeof计算数组元素个数
8.5.1 sizeof 关键字
8.5.2 计算数组的元素个数
8.6 ⼆维数组的创建
8.6.1 二维数组的概念
什么是二维数组?
8.6.2 ⼆维数组的创建
8.7 ⼆维数组的初始化
8.7.1 不完全初始化
8.7.2 完全初始化
8.7.3 按行初始化
8.7.4 省略行但不能省略列
8.8 ⼆维数组的使⽤与输入输出
8.8.1 使用
8.8.2 输入输出
8.9 ⼆维数组在内存中的存储
8.10 数组练习
8.10.1 多个字符从两端移动,向中间汇聚
8.10.2 ⼆分查找
8.1 数组概念的引入
在没有学习数组之前,如果我们想要定义10个类型相同的变量,我们需要分开写 10 行,
#include <stdio.h>
int main()
{
int a=1;
int b=2;
int c=3;
int d=4;
int e=5;
int f=6;
int g=7;
int h=8;
int i=9;
int l=10;
return 0;
)
由于逐一定义变量不仅繁琐且会导致代码占用更多的内存空间,因此我们引入了数组的概念,以更高效地管理和存储数据,那么,什么是数组呢?
数组是一个用于存储相同类型元素的集合。基于这一概念,可以提炼出两个关键点:
- 数组中可以存储一个或多个元素,但数组的元素个数不能为零。
- 数组中的所有元素必须是相同的数据类型。
数组可以分为一维数组和多维数组,其中多维数组中最常见的是二维数组
8.2.⼀维数组的创建和初始化
8.2.1 数组的创建
创建一维数组的基本语法如下:
type arr_name[常量值];
在创建数组时,可以指定数组的大小和元素类型。具体说明如下:
1
.type
:指定数组中存储的数据类型,可以是基本数据类型如char
、short
、int
、float
等,也可以是用户自定义的类型。
2.arr_name
:数组的名称,应根据实际情况赋予一个有意义的名字。
3.[]
:括号内的常量值用于指定数组的大小,大小根据实际需求决定。
例如,如果我们需要存储某个班级75人的C语言成绩,可以创建如下数组:
int C[75];
当然,也可以根据需要创建其他类型和大小的数组,例如:
char ch[20];
double score[10];
8.2.2数组的初始化
在某些情况下,数组在创建时需要指定初始值,这个过程称为数组的初始化。数组的初始化通常通过大括号 {}
来实现,将初始值放在大括号内。
完全初始化
int arr1[5]={1,2,3,4,5}
不完全初始化 // 第一个元素初始化为1,剩余元素默认初始化为0
int arr2[6] = {1};
错误的初始化 // 初始化项太多,数组大小为3,初始值有4个
int arr3[3] = {1, 2, 3, 4};
8.2.3 数组的类型
数组也是一种数据类型,属于自定义类型。数组的类型由元素类型和数组大小共同决定。
int arr1[10];
int arr2[12];
char ch[5];
在上述例子中:
arr1
数组的类型是 int[10] 注意不能只写int int是数组的元素的类型
arr2
数组的类型是 int[12]
ch
数组的类型是 char[5]
8.3 ⼀维数组的使⽤
了解了一维数组的基本语法后,我们可以用它来存储和操作数据。接下来,让我们看看如何具体使用一维数组。
8.3.1 数组下标
在C语言中,数组的元素是通过下标来访问的。数组的下标从0开始,假如数组有n个元素,那么最后一个元素的下标是n-1。下标就像是数组元素的编号。比如:
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
要访问数组中的某个元素,我们使用 []
这个下标引用操作符。比如,要访问第8个元素(下标为7),可以用 arr[7]
;要访问第4个元素(下标为3),可以用 arr[3]
。看下面的代码示例:
#include <stdio.h>
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("%d\n", arr[7]); // 输出 8
printf("%d\n", arr[3]); // 输出 4
return 0;
}
8.3.2 打印数组元素
如果我们想打印整个数组的内容,该怎么办呢?很简单,只要遍历数组的所有下标,然后依次访问每个元素。用 for
循环可以轻松实现:
#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;
}
8.3.3 输入数组元素
不仅可以访问数组元素,我们还可以根据需要向数组中输入数据。下面的示例展示了如何使用 scanf
函数输入数组的值,然后打印出来:
#include <stdio.h>
int main() {
int arr[10];
for(int i = 0; i < 10; i++) {
scanf("%d", &arr[i]);
}
for(int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
return 0;
}
8.4 ⼀维数组在内存中的存储
为了更好地理解数组,我们可以看看它们在内存中的存储方式。C语言中,数组的元素在内存中是连续存放的。我们可以打印数组元素的内存地址来验证这一点。以下代码展示了如何查看每个数组元素的地址:
#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("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
输出结果分析:
你会发现,数组元素的地址随着下标的增加而递增。对于 int
类型数组,相邻元素的地址差为4字节(因为一个 int
占用4个字节)。这说明数组在内存中是连续存放的。
8.5 sizeof计算数组元素个数
在遍历数组时,我们通常需要知道数组的元素个数。C语言中可以通过 sizeof
关键字来计算数组的元素个数。
8.5.1 sizeof
关键字
sizeof
是C语言中的一个关键字,用于计算类型或变量所占的内存空间大小。实际上,它也可以计算数组的大小。
来看一个简单的示例:
#include <stdio.h>
int main() {
int arr[10] = {0};
printf("数组的总大小: %lu 字节\n", sizeof(arr));
return 0;
}
在这个例子中,sizeof(arr)
计算的是数组 arr
所占内存的总大小,单位是字节。对于 int
类型的数组,每个元素占4个字节,所以输出结果是40字节。
8.5.2 计算数组的元素个数
因为数组中的所有元素类型相同,所以我们可以通过计算单个元素的大小,再除以整个数组的大小,得到数组的元素个数。例如:
#include <stdio.h>
int main() {
int arr[10] = {0};
int elementSize = sizeof(arr[0]);
printf("一个元素的大小: %lu 字节\n", elementSize);
return 0;
}
接下来,我们可以计算数组的元素个数:
#include <stdio.h>
int main() {
int arr[10] = {0};
int numOfElements = sizeof(arr) / sizeof(arr[0]);
printf("数组的元素个数: %d\n", numOfElements);
return 0;
}
这种方法非常实用,不论数组大小如何变化,计算出的元素个数都能自动更新。这种灵活性使代码更具适应性,避免了手动计算的麻烦。
8.6 ⼆维数组的创建
8.6.1 二维数组的概念
我们之前学习过的一维数组是指数组的元素都是基本数据类型(如 int
、char
等)。当我们把一个一维数组作为另一个数组的元素时,就得到了二维数组。再进一步,如果把二维数组作为元素,这时就得到了三维数组。所有超过二维的数组统称为多维数组
什么是二维数组?
二维数组可以看作是一个“数组的数组”。换句话说,二维数组的每个元素都是一个一维数组。
8.6.2 ⼆维数组的创建
创建方法
type arr_name[常量值1][常量值2];
例如:
int arr[3][5];
double data[2][8];
8.7 ⼆维数组的初始化
当我们创建一个变量或数组时,如果同时给它赋值,这个过程就称为初始化。对于二维数组,初始化的方式与一维数组类似,也是使用大括号 {}
来进行。
8.7.1 不完全初始化
在不完全初始化中,只为数组的部分元素赋值,未赋值的部分会自动初始化为零。
#include <stdio.h>
int main() {
int arr1[3][5] = {1, 2}; // 只有前两个元素被初始化,其他元素自动初始化为 0
int arr2[3][5] = {0}; // 所有元素都被初始化为 0
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr1[i][j]);
}
printf("\n");
}
return 0;
}
8.7.2 完全初始化
完全初始化时,所有元素都显式赋值。每个元素都指定了一个值,数组中没有剩余的元素需要自动初始化。
#include <stdio.h>
int main() {
int arr3[3][5] = {
{1, 2, 3, 4, 5},
{2, 3, 4, 5, 6},
{3, 4, 5, 6, 7}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr3[i][j]);
}
printf("\n");
}
return 0;
}
8.7.3 按行初始化
按行初始化时,每一行可以单独使用一对大括号进行初始化。这种方式更清晰地展示了数组的行结构。
#include <stdio.h>
int main() {
int arr4[3][5] = {
{1, 2}, // 第一行的前两个元素被赋值
{3, 4}, // 第二行的前两个元素被赋值
{5, 6} // 第三行的前两个元素被赋值
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr4[i][j]);
}
printf("\n");
}
return 0;
}
8.7.4 省略行但不能省略列
在初始化时,可以省略数组的行数,但不能省略列数。省略行数时,编译器会根据初始值的数量自动确定行数。
#include <stdio.h>
int main() {
int arr5[][5] = {1, 2, 3}; // 自动推导出行数,这里是1行
int arr6[][5] = {1, 2, 3, 4, 5, 6, 7}; // 自动推导出行数,这里是2行
int arr7[][5] = { // 3行
{1, 2},
{3, 4},
{5, 6}
};
printf("arr5: \n");
for (int i = 0; i < 1; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr5[i][j]);
}
printf("\n");
}
printf("arr6: \n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr6[i][j]);
}
printf("\n");
}
printf("arr7: \n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr7[i][j]);
}
printf("\n");
}
return 0;
}
这种灵活的初始化方式可以让你的代码更简洁,并且更加适应各种不同的需求
8.8 ⼆维数组的使⽤与输入输出
8.8.1 使用
现在我们已经了解了如何访问二维数组的单个元素,接下来我们看看如何处理整个二维数组。关键是要以一定的结构生成所有可能的行和列索引。以数组 arr
为例,它有3行5列,行的范围是0到2,列的范围是0到4。我们可以使用嵌套循环来生成这些索引。
#include <stdio.h>
int main()
{
int arr[3][5] = {
{1,2,3,4,5},{6,7,8,9,1},{2,3,4,5,6}
};
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
return 0;
}
8.8.2 输入输出
要访问二维数组的所有元素,只需按行和列的顺序生成对应的下标即可。以 arr
数组为例,它有3行5列,行的范围是0到2,列的范围是0到4。我们可以使用嵌套的循环来生成这些下标,并遍历整个数组。
#include <stdio.h>
int main()
{
int arr[3][5] = {
{1,2,3,4,5},{6,7,8,9,1},{2,3,4,5,6}
};
int i = 0;
int j = 0;
for (i = 0; i < 3; i++) //输入
{
for (j = 0; j < 5; j++)
{
scanf("%d", &arr[i][j]);
}
}
for (i = 0; i < 3; i++) //输出
{
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
8.9 ⼆维数组在内存中的存储
为了更好地理解二维数组在内存中的存储方式,我们可以通过打印出数组中每个元素的地址来进行研究。以下是示例代码:
#include <stdio.h>
int main()
{
int arr[3][5] = {
{1,2,3,4,5},{6,7,8,9,1},{2,3,4,5,6}
};
int i = 0;
int 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]);
}
printf("\n");
return 0;
}
通过结果可知:
1.每一行内部的元素地址是连续的,地址之间相差4个字节(假设整数是4字节)。
2.相邻行之间的元素地址也相差4个字节。
这表明,二维数组在内存中是按行连续存储的。了解这个布局有助于我们更深入地学习如何使用指针来访问和操作数组。
8.10 数组练习
8.10.1 多个字符从两端移动,向中间汇聚
要实现字符从两端向中间汇聚的效果,可以使用以下代码。这个代码展示了如何逐步将字符从一个字符串(arr1
)移动到另一个字符串(arr2
)中,并在每一步打印出当前的状态。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "Wishing you happiness every day";
char arr2[] = "###############################";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}
8.10.2 ⼆分查找
在⼀个升序的数组中查找指定的数字n,很容易想到的⽅法就是遍历数组,但是这种⽅法效率⽐较低。 ⽐如我买了⼀本书,你好奇问我多少钱,我说不超过50元。你还是好奇,你想知道到底多少,我就让 你猜,你会怎么猜?你会1,2,3,4...这样猜吗?显然很慢;⼀般你都会猜中间数字,⽐如:55,然 后看⼤了还是⼩了, 二分查找是一种高效的搜索算法,用于在有序数组中查找指定元素。它通过每次将查找范围减半来快速找到目标元素。
#include <stdio.h>
int main() {
char arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
int k = 6;
int find = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
int mid = 0; // 在循环外声明 mid
while (left <= right)
{
mid = (left + right) / 2; // 更新 mid 的值
if (arr[mid] < k) {
left = mid + 1; // 中间值小于目标值, 将左边界更新为中间值的下一位
}
else if (arr[mid] > k) {
right = mid - 1; // 中间值大于目标值, 将右边界更新为中间值的前一位
}
else {
find = 1;
break;
}
}
if (find == 1)
printf("找到了, 下标是 %d\n", mid);
else
printf("找不到\n");
return 0;
}
mid = left+(right-left)/2;