前言
为何要引入数组?
假设你有一箱苹果,你想要用C语言来管理这些苹果。
如果你将每个苹果看作是一个数据项,那么使用数组就像是给这些苹果编号并把它们放进了一个有序的箱子里。
那么这样做有什么好处呢?
- 组织和访问:把苹果放进数组中就像是把它们放进了一个有序的箱子里一样。你可以轻松地根据编号找到箱子中的特定苹果。
- 内存管理:数组中的苹果是连续存放的,就像是连续编号的箱子一样。这样做使得查找和管理苹果变得更加高效。
- 循环和迭代:如果你想检查每个苹果的品质,你可以像一个人一个人地检查箱子里的苹果一样,使用循环来遍历整个数组。
- 参数传递:假设你想要把箱子里的苹果交给另一个人来检查,你只需要将整个箱子传递给他即可,而不需要把每个苹果都逐个交给他。
- 数据结构:这个箱子可以看作是一个简单的数据结构,你可以对它进行各种操作,比如添加苹果、删除苹果、计算苹果的总数等等。
一、数组的特点
对于数组的特点,首先要知道的是 :
1、数组中的元素是相同类型的;
2、数组在内存中是连续存储的;
3、数组在创建的时候必须指定大小,数组的大小在运行时是固定不变的;
4、数组中的元素是按照顺序存储的,每个元素都有一个唯一的索引,因此可以通过索引来随机访问数组中的元素;
5、在函数调用中,如果将数组作为参数传递给函数,实际上传递的是数组的地址,而不是数组的副本。这意味着对传递的数组进行修改会影响到原始数组。
6、数组通常在编译时就会被分配好内存空间,这种分配方式称为静态分配。
二、一维数组
2.1 创建与初始化
一维数组创建很简单喽~
type arrayName[arraySize];
/*
* type 是数组中元素的数据类型。
* arrayName 是数组的名称。
* arraySize 是数组的大小,即数组中元素的数量。
*/
一维数组的初始化看这里
2.1.1 显式初始化
//显式地提供数组的初始值。
int numbers[5] = {1, 2, 3, 4, 5};
2.1.2 部分初始化
//只初始化数组的一部分元素,其余元素会被自动初始化为0。
int numbers[5] = {1, 2}; // numbers数组的前两个元素为1和2,剩余元素自动初始化为0
2.1.3 使用循环初始化
//通过循环为数组的每个元素赋值。
int numbers[5];
for (int i = 0; i < 5; i++) {
numbers[i] = i + 1;
}
2.1.4 使用字符串初始化字符数组
//使用字符串字面值来初始化字符数组。
char str[] = "Hello";
2.1.5 使用指针初始化
//可以使用指针来初始化数组的元素
int numbers[5];
int *ptr = numbers;
for (int i = 0; i < 5; i++) {
*ptr++ = i + 1;
}
特别注意,补充内容
//这两者是等价的
int numbers[5] = {0};
int numbers[5] = {0,0,0,0,0};
//在对全部数组元素赋值时,由于数据的个数已经确定,因此可以不指定数组长度
int numbers[5] = {1,2,3,4,5}; //与下面写法一样
int numbers[] = {1,2,3,4,5};
前面说了“数组在内存中是连续存储的”,那我们打印一下数组中元素的地址看看吧。
//打印数组中元素的地址
#include <stdio.h>
int main()
{
int numbers[10];//创建一个数组
int i;
//对数组进行初始化
for(i = 0;i < 10;i++){
numbers[i] = i +100;
}
puts("数组初始化完成");
//打印数组中各个元素的地址
for(i = 0;i < 10;i++){
printf("numbers[%d]d的地址:%p\n",i,&numbers[i]);
}
puts("done");
return 0;
}
输出的结果在这嘞,果然内存是连续的耶,那为什么相邻元素之间的地址恰好是4呢?这意味着这个数组中的每个元素占用4个字节的内存空间。这符合一般情况下的整型数据(int)在32位系统中占用4个字节的标准,而在64位系统中可能占用8个字节。
数组初始化完成
numbers[0]d的地址:0x7ffd8827d300
numbers[1]d的地址:0x7ffd8827d304
numbers[2]d的地址:0x7ffd8827d308
numbers[3]d的地址:0x7ffd8827d30c
numbers[4]d的地址:0x7ffd8827d310
numbers[5]d的地址:0x7ffd8827d314
numbers[6]d的地址:0x7ffd8827d318
numbers[7]d的地址:0x7ffd8827d31c
numbers[8]d的地址:0x7ffd8827d320
numbers[9]d的地址:0x7ffd8827d324
done
还有一个问题:我们怎么知道数组中有多少个元素呢??
解决办法:
//size:数组中的元素个数
int size = sizeof(numbers) / sizeof(numbers[0]);
举个栗子吧
//计算数组中的元素个数
#include <stdio.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
int main()
{
int numbers[] = {2,4,2,43,23,234,13,4,4,5,6,3,6,4,32,413,123};//这里面有17个元素,我数过了
int size = ARRAY_SIZE(numbers);
printf("数组中元素的数量为:%d\n", size);
return 0;
}
运行结果在这:
数组中元素的数量为:17
三、二维数组
3.1 创建与初始化
3.1.1 逐个赋值初始化
二维数组其实就是特殊的一维数组,下面是对二维数组指定大小,然后逐个进行赋值的方式。
#include <stdio.h>
#define ROWS 3
#define COLS 4
int main() {
//方法一:大括号内部还有大括号
int array1[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
//方法二:大括号内部没有大括号
int array2[ROWS][COLS] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
// 输出数组内容
printf("array1二维数组内容:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", array1[i][j]);
}
printf("\n");//换行
}
printf("array2二维数组内容:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", array2[i][j]);
}
printf("\n");
}
return 0;
}
程序运行的如下:
array1二维数组内容:
1 2 3 4
5 6 7 8
9 10 11 12
array2二维数组内容:
12 11 10 9
8 7 6 5
4 3 2 1
注意:如果对全部元素都赋初值,则定义数组时可以对第一维的长度不指定,但是第二维的长度不能省。
int array[3][4] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
//与下面等价
int array[][4] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
3.1.2 使用循环进行初始化
#include<stdio.h>
#define ROWS 3
#define COLS 4
int main() {
int array[ROWS][COLS];
int counter = 1;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
array[i][j] = counter;
counter++;
}
}
printf("二维数组内容:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", array[i][j]);
}
printf("\n");
}
return 0;
}
3.1.3 部分初始化
下面介绍对二维数组的部分初始化,没有进行赋值的元素默认是0。
#include<stdio.h>
#define ROWS 3
#define COLS 4
int main() {
//可以对数组中的某个元素进行初始化
int array[ROWS][COLS] = {{1}, {2, 20}, {3, 30, 40}};
//同样能够对二维数组中每一行的第一个元素进行赋值
//int array[ROWS][COLS] = {{1}, {2}, {3}};
printf("二维数组内容:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", array[i][j]);
}
printf("\n");
}
return 0;
}
运行结果
二维数组内容:
1 0 0 0
2 20 0 0
3 30 40 0
四、编程实例
学习了数组,那就找两个实例练习一下吧
4.1 冒泡排序算法
首先要知道什么叫做冒泡排序?下面举一个栗子说明一下原理。
假设你手里有一叠扑克牌,而你想要把这叠扑克牌按照从小到大的顺序排列(从大到小也是一样的原理哦)。你可以使用冒泡排序的方式来完成这个任务。
比较相邻的扑克牌:首先,你从第一张牌开始,逐个比较相邻的两张牌。如果一张牌比另一张牌大,那么你就交换它们的位置,把较大的牌往后移动。
重复这个过程:接着,你在第一次操作之后,继续从第二张牌开始,再次比较相邻的两张牌,直到最后一张牌。在这个过程中,较大的牌会逐渐“冒泡”到牌堆的顶部。
完成一轮冒泡:一轮比较完成后,你会发现最大的牌已经在最顶部了。
重复以上步骤:接着,你重复这个过程,但是这次你不再考虑已经排好序的牌,而是只考虑未排序的牌。每一轮都会确定出一个最大的牌,并将其移动到牌堆的顶部。
排序完成:当所有的牌都已经排好序,也就意味着整叠扑克牌已经按照从小到大的顺序排列好了。
下面通过一张图来解释一下冒泡排序算法的原理,假设最开始的元素分别是5 4 8 3
,我们想要从小到大对这些元素进行排序。
- 首先我们将前两个元素进行比较,5比4大,则交换他们位置,第一轮的第一次比较结束;
- 然后比较第二、第三个元素,8比5大,不用做改变,第一轮的第二次比较结束;
- 最后比较第三、第四个元素,3比8小,则交换他们位置,第一轮的第三次比较结束,同时第一轮比较结束。
以此类推,进行第二轮比较、第三轮比较,直至所有元素从小到大排列。
代码实现如下:
#include <stdio.h>
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换arr[j]和arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);//计算数组中的元素个数
bubbleSort(arr, n);
printf("排序后的数组:\n");
//打印排序之后的数组值
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
运行结果
排序后的数组:
11 12 22 25 34 64 90
4.2 旋转二维数组中的元素
编写一个C程序,实现将一个3x3大小的二维数组中的元素顺时针旋转90度的功能。
#include <stdio.h>
void rotateMatrix(int matrix[3][3]) {
int temp[3][3];
// 先进行转置操作
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
temp[i][j] = matrix[j][i];
}
}
// 再对每一行进行逆序操作
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix[i][j] = temp[i][2 - j];
}
}
}
int main() {
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
printf("原始矩阵:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
rotateMatrix(matrix);
printf("旋转后的矩阵:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
运行结果:
原始矩阵:
1 2 3
4 5 6
7 8 9
旋转后的矩阵:
7 4 1
8 5 2
9 6 3