概念:数组是指一组**相同类型元素**的集合。
从这个概念中,我们可以知道以下两个信息:
(1):数组中存放的是1个或多个数据,但是数组的元素个数不能为0。
(2):数组中存放的多个数据,其数据类型是相同的。
数组分为一维数组和多维数组,多维数组一般比较常见的是二维数组。
type_t arr_name [const_n]
//type_t 指数组的元素类型
//const_n 为一个常量表达式用于指定数组的大小
//[]中的常量值是用于指定数组的大小,这个数组的大小要根据实际的需求来进行指定。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[5];
int arr2[3 + 2];
int n = 0;
scanf("%d\n",&n);
//变长数组
char arr3[n];
return 0;
}
PS:在创建数组时,我们通常使用一个常量表达式用于指定数组的大小,而不是变量,原因在于,在c99之前数组只能是常量指定大小,在C99之后引入了变长数组的概念,数组的大小是可以使用变量来指定滴。博主使用的vs2022是不支持变长数组滴,希望uu们注意。
有时候,数组在创建的时候,我们需要给定一些初始值,这种操作就被称作初始化。那么如何对数组进行初始化呢?数组的初始化一般使用大括号,将数据放在大括号中。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//完全初始化
int arr1[5] = { 1,2,3,4,5 };
//不完全初始化,第一个元素初始化为1,剩余的元素默认初始化为0
int arr2[8] = { 1 };
//未指定大小,则根据数组中元素的值来确定数组的元素个数
int arr3[] ={1,2,3,4,5};
//错误的初始化----初始化项太多,超过了数组的元素个数
int arr4[3] = {1,2,3,4};
return 0;
}
学习了?维数组的基本语法,?维数组可以存放数据,存放数据的?的是对数据的操作,那我们如何使??维数组呢?
C语言规定数组中的每个元素是有对应的下标的,下标是从0开始的,假设数组有n个元素,最后一个元素的下标识n-1,下标就相当于数组元素的标号。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
return 0;
}
在C语言中数组的访问提供了一个操作符[],这个操作符叫:下标引用操作符有了下标访问操作符,我们就可以轻松地访问到数组的元素了,譬如我们想访问下标为7的元素,就可以使用arr[7],访问下标为3的元素,可以使用arr[3]。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", arr[7]);
printf("%d\n", arr[3]);
return 0;
}
有了前面的知识,我们其实使用数组基本上没有什么障碍了,如果想要深入了解数组,我们最好能了解一下数组在内存中的存储。 接下来我们将使用如下代码来打印数组在内存中的存储。
#define _CRT_SECURE_NO_WARNINGS
#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++)
{
//%p用于打印指针的值,将地址以16进制的形式输出
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
从输出的结果我们可以发现,数组随着下标的增长,地址是由小到大变化的,并且每两个相邻的元素之间相差4(因为一个整型占4个字节)。因此,我们可以得出这样一个结论:数组在内存中是连续存储的,并且随着下标的增长,地址是由低到高变化的,从低地址到高地址。
概念:前面博主所讲的数组被称作一维数组,数组的元素类型都是内置类型的,如果我们把一维数组作为数组的元素,这个时候为**二维数组**,二维数组作为数组元素的数组被称为**三维数组**,二维数组以上的数组统称为**多维数组**。
PS:二维数组可以与我们在大学中线性代数中的矩阵进行类比哦
type arr_name[常量值1][常量值2]
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//3表示数组有3行,5表示每一行有5个元素,即3行5列
int arr1[3][5];
double arr2[2][8];
return 0;
}
二维数组的初始化,和一维数组一样,也是使用大括号初始化滴!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//不完全初始化
int arr1[3][5] = { 1,2 };
int arr2[3][5] = { 0 };
//完全初始化
int arr3[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
return 0;
}
ps:二维数组在初始化时,行可以省略,但是列不能够省略,当行省略时,则是根据数组中的元素的值来确定行。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[][5] = { 1,2,3 };
int arr2[][5] = { 1,2,3,4,5,6 };
int arr3[][5] = { {1,2},{3,4},{5,6} };
return 0;
}
当我们掌握了二维数组的创建与初始化后,那我们怎么样去使用二维数组呢?其实二维数组的访问也是通过下标的形式去访问的,二维数组是有行和列的,只要锁定了行和列就能唯一锁定数组中的一个元素。
C语言规定,二维数组的行是从0开始的,列也是从0开始的。
通过这段代码实现了对二维数组的访问,只要我们能够按照一定的规定产生所有的行和列,就能够实现对二维数组的访问。
和一维数组一样,如果想研究二维数组在内存中的存储方式,我们也是可以打印出数组所有元素的地址滴。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[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("&arr[%d][%d] = %p ",i,j,&arr[i][j]);
printf("\n");
}
printf("\n");
}
return 0;
}
从输出的结果来看,每一行的内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置的两个元素(如:arr[1][4]和arr[2][0])之间也是差4个字节,因此二维数组中的每个元素都是连续存放的。如下图所示
数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
因此数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组的合法空间的访问。
ps:C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。
因此在写代码时,最好自己做越界的检查。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5 };
for (int i = 0; i <= 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
什么是数组名呢?博主将通过下面这段代码来讲解数组名
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5 };
printf(" arr = %p\n", arr);
printf("&arr[0] = %p\n", &arr[0]);
printf(" *arr = %d\n", *arr);
return 0;
}
通过这段代码的运行结果我们可以得出,数组名表示数组首元素的地址。OK,如果数组名为首元素的地址,那么再看下面这段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5 };
printf("%d\n", sizeof(arr));
return 0;
}
不是说数组名为首元素的地址吗,那这里为什么输出的是40呢?
> 补充:(1):sizeof(数组名),计算的是整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
(2):&数组名,取出的是整个数组的地址。&数组名,数组名表示整个数组。
除了这两种特殊情况,在其他情况,所有的数组名均表示数组首元素的地址。
有了我们数组以及函数等等的相关知识,接下来博主将为uu们介绍一种排序算法----->冒泡排序。这里博主以排成升序为例。
核心思想:两两相邻元素进行比较。
我们通过上面的动图可以发现,每进行一趟冒泡排序,就可以让元素到达自己应该处的位置。假设我们要对10个数进行排序,那么应该要进行几趟冒泡排序呢?答案应该是9趟,因为单独的一个数我们可以认为它是有序滴,这里家人们要注意哈。首先我们来看单趟的冒泡排序。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
//求出数组中的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
for (int j = 0; j < sz - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
return 0;
}
如果左边的元素大于右边的元素,那么这两个数就进行交换,直到该元素到达自己应该所处的位置,这是一趟冒泡排序,但是这里有10个数呀,要进行9趟冒泡排序,并且每次进行一趟冒泡排序后,趟数要减少,因此就要使用双层的嵌套循环来进行实现!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
//求出数组中的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
printf("排序前:>");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
//确定总趟数
for (int i = 0; i < sz - 1; i++)
{
//每进行一次冒泡排序,趟数要减少,因此- i
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
printf("\n");
printf("排序后:>");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
好啦,家人们,关数组这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴滴给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!