C语言数组

文章目录

    • 1:一维数组的创建和初始化
      • 1.1 创建方式
      • 1.2:一维数组的初始化
      • 1.3:一维数组的使用
        • 1.3.1:数组下标
      • 1.4:一维数组在内存中的存储
    • 2:二维数组的创建和初始化
      • 2.1:二维数组的创建
      • 2.2:二维数组的初始化
      • 2.3:二维数组的下标
      • 2.4:二维数组在内存中的存储
    • 3:数组越界
    • 4:数组名
    • 5:冒泡排序

嘿嘿,家人们,今天咱们来详细介绍下C语言中的数组,好啦,废话不多讲,开干!

1:一维数组的创建和初始化

概念:数组是指一组**相同类型元素**的集合。

从这个概念中,我们可以知道以下两个信息:
(1):数组中存放的是1个或多个数据,但是数组的元素个数不能为0。
(2):数组中存放的多个数据,其数据类型是相同的。
数组分为一维数组和多维数组,多维数组一般比较常见的是二维数组。

1.1 创建方式

type_t arr_name [const_n]
//type_t 指数组的元素类型
//const_n 为一个常量表达式用于指定数组的大小
//[]中的常量值是用于指定数组的大小,这个数组的大小要根据实际的需求来进行指定。

#define  _CRT_SECURE_NO_WARNINGS
#include 
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们注意。

C语言数组_第1张图片

1.2:一维数组的初始化

有时候,数组在创建的时候,我们需要给定一些初始值,这种操作就被称作初始化。那么如何对数组进行初始化呢?数组的初始化一般使用大括号,将数据放在大括号中。
#define  _CRT_SECURE_NO_WARNINGS
#include 
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语言数组_第2张图片

1.3:一维数组的使用

学习了⼀维数组的基本语法,⼀维数组可以存放数据,存放数据的⽬的是对数据的操作,那我们如何使⽤⼀维数组呢?
1.3.1:数组下标

C语言规定数组中的每个元素是有对应的下标的,下标是从0开始的,假设数组有n个元素,最后一个元素的下标识n-1,下标就相当于数组元素的标号。

#define  _CRT_SECURE_NO_WARNINGS
#include 
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 
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;
}

C语言数组_第3张图片

1.4:一维数组在内存中的存储

有了前面的知识,我们其实使用数组基本上没有什么障碍了,如果想要深入了解数组,我们最好能了解一下数组在内存中的存储。 接下来我们将使用如下代码来打印数组在内存中的存储。

#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言数组_第4张图片

从输出的结果我们可以发现,数组随着下标的增长,地址是由小到大变化的,并且每两个相邻的元素之间相差4(因为一个整型占4个字节)。因此,我们可以得出这样一个结论:数组在内存中是连续存储的,并且随着下标的增长,地址是由低到高变化的,从低地址到高地址

2:二维数组的创建和初始化

概念:前面博主所讲的数组被称作一维数组,数组的元素类型都是内置类型的,如果我们把一维数组作为数组的元素,这个时候为**二维数组**,二维数组作为数组元素的数组被称为**三维数组**,二维数组以上的数组统称为**多维数组**。

PS:二维数组可以与我们在大学中线性代数中的矩阵进行类比哦

2.1:二维数组的创建

type arr_name[常量值1][常量值2]

#define  _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	//3表示数组有3行,5表示每一行有5个元素,即3行5列
	int arr1[3][5];
	double arr2[2][8];
	return 0;
}

2.2:二维数组的初始化

二维数组的初始化,和一维数组一样,也是使用大括号初始化滴!
#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言数组_第5张图片

ps:二维数组在初始化时,行可以省略,但是列不能够省略,当行省略时,则是根据数组中的元素的值来确定行。

#define  _CRT_SECURE_NO_WARNINGS
#include 
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语言数组_第6张图片

2.3:二维数组的下标

当我们掌握了二维数组的创建与初始化后,那我们怎么样去使用二维数组呢?其实二维数组的访问也是通过下标的形式去访问的,二维数组是有行和列的,只要锁定了行和列就能唯一锁定数组中的一个元素。
C语言规定,二维数组的行是从0开始的,列也是从0开始的。

C语言数组_第7张图片

通过这段代码实现了对二维数组的访问,只要我们能够按照一定的规定产生所有的行和列,就能够实现对二维数组的访问。

2.4:二维数组在内存中的存储

和一维数组一样,如果想研究二维数组在内存中的存储方式,我们也是可以打印出数组所有元素的地址滴。
#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言数组_第8张图片

从输出的结果来看,每一行的内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置的两个元素(如:arr[1][4]和arr[2][0])之间也是差4个字节,因此二维数组中的每个元素都是连续存放的。如下图所示

C语言数组_第9张图片

3:数组越界

数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
因此数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组的合法空间的访问。
ps:C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。
因此在写代码时,最好自己做越界的检查。

#define  _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int arr[10] = { 1,2,3,4,5 };

	for (int i = 0; i <= 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

C语言数组_第10张图片

4:数组名

什么是数组名呢?博主将通过下面这段代码来讲解数组名
#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言数组_第11张图片

通过这段代码的运行结果我们可以得出,数组名表示数组首元素的地址。OK,如果数组名为首元素的地址,那么再看下面这段代码。

#define  _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int arr[10] = { 1,2,3,4,5 };
	printf("%d\n", sizeof(arr));
	return 0;
}

C语言数组_第12张图片

不是说数组名为首元素的地址吗,那这里为什么输出的是40呢?
> 补充:(1):sizeof(数组名),计算的是整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
(2):&数组名,取出的是整个数组的地址。&数组名,数组名表示整个数组。
除了这两种特殊情况,在其他情况,所有的数组名均表示数组首元素的地址。

5:冒泡排序

有了我们数组以及函数等等的相关知识,接下来博主将为uu们介绍一种排序算法----->冒泡排序。这里博主以排成升序为例。

核心思想:两两相邻元素进行比较。
C语言数组_第13张图片

我们通过上面的动图可以发现,每进行一趟冒泡排序,就可以让元素到达自己应该处的位置。假设我们要对10个数进行排序,那么应该要进行几趟冒泡排序呢?答案应该是9趟,因为单独的一个数我们可以认为它是有序滴,这里家人们要注意哈。首先我们来看单趟的冒泡排序

#define  _CRT_SECURE_NO_WARNINGS
#include 
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 
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;
}

C语言数组_第14张图片

好啦,家人们,关数组这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴滴给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!

你可能感兴趣的:(c语言)