【C语言基础学习笔记】四、数组详解(超详细!超硬核!)

本章前言
在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。
简化记忆方式:数组是一组相同类型元素的集合。


文章目录

  • 一维数组的创建和初始化
      • 一维数组的创建
      • 一维数组的初始化
      • 一维数组的使用
      • 一维数组在内存中的存储
  • 二维数组的创建和初始化
      • 二维数组的创建
      • 二维数组的初始化
      • 二维数组的使用
      • 二维数组在内存中的存储
  • 数组作为函数参数
  • 思考:数组名到底是什么?
  • 三维数组及多维数组


一维数组的创建和初始化

一维数组的创建

数组的创建方式 :

type_t arr_name[const_n];
//type_t是指数组的元素类型
//arr_name是数组名
//const_n是一个常量表达式,用来指定数组的大小

注∶数组创建,[ ]中要给一个常量才可以,不能使用变量。
例如:

//代码1 
int arr1[10];
char arr2[9];
double arr3[8];
//以上数组可正常创建

//代码2
int count = 10;
int arr4[count];
//该数组不能正常创建,[]中应该为常量,不能为变量

提示:有些编译器中[ ]中出现了变量不报错,也正常编译执行,那是因为该编译器支持C99语法标准。在C99语法标准中有变长数组的概念-- -
数组[]中可以是变量。
一般我们常用的编译器,如Viual Studio(VS)系列编译器是不支持C99语法标准的。


一维数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值。

int arr1[10] = {
      1,2,3 };//不完全初始化
int arr2[] = {
      1,2,3,4 };
int arr3[5] = {
      12345 }; //完全初始化
char arr4[3] = i'a ', 98'c'};
char arr5[] = {
      'a ', ' b', 'c ' };
char arr6[] = "abcdef";

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第1张图片
这里我们要注意区分

char ch2[] = {
      'b','i','t' };
char ch4[] = "bit";

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第2张图片

在使用printf打印字符以及strlen求字符串长度时候,遇到’\0’才停止,没遇到’\0’之前不停止。


一维数组的使用

对于数组的使用我们之前介绍了一个操作符︰[ ]下标引用操作符。它其实就数组访问的操作符。我们来看代码︰

#include 
int main()
{
     
	int arr[10] = {
      0 };//数组的不完全初始化//计算数组的元素个数
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	//对数组内容赋值,数组是使用下标来访问的,下标从0开始。
	for (i = 0; i < 10; i++)//这里写10,好不好?
		arr[i] = i;
	//输出数组的内容
	for (i = 0; i < 10; ++i)
	{
     
		printf("%d ", arr[i]);
	}
	return 0;
}

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第3张图片

总结:
1.数组是使用下标来访问的,下标是从O开始。
2.数组的大小可以通过计算得到。
int arr[10]; int sz = sizeof(arr) / sizeof(arr[0]);


一维数组在内存中的存储

接下来我们探讨数组在内存中的存储。看下图︰
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第4张图片

通过观察我们可以看到:
1.一维数组在内存中是连续存放的
2.随着数组下标的增长,地址是由低到高变化的

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第5张图片
数组地址连续存放有什么实际意义或作用吗?
看下面这个例子:
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第6张图片
这个例子可以很好说明刚刚的问题,正因为数组是连续存放的,通过数组首元素的地址往后找可以找到每一个数组对应的元素!


二维数组的创建和初始化

二维数组的创建

//二维数组创建
int arr1[3][4];
char arr2[3][4];
double arr3[4][5];

当我们创建一个二维数组int arr[3][4]后,在我们的脑海中要形式对应的三行四列二维数组,这个数组的每个元素都是int类型。(二维数组有行列之分)
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第7张图片


二维数组的初始化

//初始化---创建的同时给赋值
方式①
int arr1[3][4] = {
      1,2,3,4,5,6,7,8,9,10,11,12 };//完全初始化

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第8张图片

三行四列的数组arr1有12个元素,我们可以在初始化的时候直接用大括号将12个元素括起来赋值给arr1。


思考:如果说我们给的元素个数大于12个或小于12个会发生什么呢?
(1)大于12个元素时:程序运行时,编译报错,无法正常执行,报错原因就是初始值设定项太多。从这个报错我们大概就能知道对于数组创建,编译器会检查初始项是否大于数组的长度。这个其实也很好理解,数组本质也是变量,变量的创建需要向内存申请空间,如果初始值设定项占用的空间大于数组申请的空间,就会“越界访问”(比如数组申请一块40个字节大小的空间,我们在设置初始项的时候给了11个int类型的值,也就是44个字节,超过数组本身可以访问空间的大小了)。
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第9张图片
(2)小于12个元素时:数组会进行不完全初始化,前几项会赋值成初始化的值,后面几项会自动赋初始值0填充。(如果是字符数组,会用\0填充,\0的ASCII码值是0)
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第10张图片


方式②
二维数组可以看作由多个一维数组组成,所以我们可以这样初始化:
int arr1[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第11张图片
(1)在使用方式②初始化int arr1[3][4]时,如果初始的行或者列超限会怎么样呢?
可以看到会出现类似方式①越界的问题。这个其实也比较好理解,int arr[3][4],二维数组arr是一个三行四列的形式,其实将二维数组arr[3][4]看作有三个一位数组arr[4]组成的数组,这个数组有三个元素,每个元素都是由四个元素组成的数组。如果初始的行或者列超限就会导致类似方式①说到的越界访问的问题,所以报错编译不过去!
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第12张图片
(2)初始化的时候不完全赋值会怎么样呢?
数组会进行不完全初始化,前几项会赋值成初始化的值,后面几项会自动赋初始值0填充。(如果是字符数组,会用\0填充,\0的ASCII码值是0)
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第13张图片
注意:二维数组在创建的时候行可以省略,列不能省略(第一个[ ]中的值可以不写,第二个[ ]值必须写)
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第14张图片


二维数组的使用

二维数组的使用方式也是通过下标的方式。二维数组的行和列下标都是从0开始的:
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第15张图片

#include
int main()
{
     
	int arr[3][4] = {
      1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
     
		for (j = 0; j < 4; j++)
		{
     
			printf("%3d", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第16张图片


二维数组在内存中的存储

之前我们提到在创建二维数组的时候,会在脑海中将其想象成一个二维表(有行和列),但是二维数组在内存的存储形式是什么样的呢?跟我们刚刚想的二维表形式一样存储方式吗?
为了研究二维数组的存储,这里我们可以尝试将二维数组的每个元素都打印出来:
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第17张图片
我们可以看到二维数组的每个元素之间均相差4个字节,所以二维数组在内存中也是连续存放的!
这个连续存放有两层含义:1.每一行内部的元素连续存放 2.行与行之间连续存放
在这里插入图片描述


思考:了解二维数组在内存中是连续存放的有什么作用?
1、在说int arr[][4]; 这个例子的时候,我们说二维数组行可以省略,列不可以省略。为什么列不可以省略呢?
当列确定的时候,我们才能知道一行有多少个元素,才能知道第二行从哪里开始,否则的话,这个二维数组就不是确定的
2、只有当二维数组是连续存放的方式,当我拿到二维数组首元素地址的时候,就可以依次访问到这个数组的所有元素。
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第18张图片
二维数组arr[3][4]可以看作由三个一维数组组成,这三个一位数组的数组名分别为arr[0], arr[1], arr[2]
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第19张图片

数组通过数组名 + [下标]访问其成员,由此我们可知arr[0], arr[1], arr[2]也是数组名。
该二维数组的数组名:arr
二维数组的第一行数组名:arr[0]
二维数组的第二行数组名:arr[1]
二维数组的第三行数组名:arr[2]


数组作为函数参数

我们在写代码的时候,往往会将数组作为参数传给函数,比如:我们要实现一个冒泡排序函数将一个整型数组排序。那我们将这样使用函数:

冒泡排序的思想:两两相邻的元素进行比较,并且可能的话需要交换。

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第20张图片

一趟解决一个数字的排序问题,第一趟最大值9出现到最右侧,第二趟8到右侧第二位
10个数字,需要进行9躺冒泡排序
n个数字,需要进行 n - 1躺

一趟冒泡排序内部:
第一趟:10个数字待排序,9对比较
第二趟:9个数字待排序,8对比较
第三趟:8个数字待排序,7对比较
……
第九趟:2个数字待排序,1对比较

代码实现:

#include
void print(int arr[], int sz)
{
     
	int i = 0;
	for (i = 0; i < sz; i++)
	{
     
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void bubble_sort(int arr[], int sz)
{
     
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)//确定冒泡排序的趟数
	{
     
		for (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;
			}
		}
	}
}
int main()
{
     
	int arr[] = {
      9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前:");
	print(arr, sz);
	//冒泡排序,进行升序排列
	bubble_sort(arr, sz);
	printf("排序后:");
	print(arr, sz);
	return 0;
}

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第21张图片

冒泡排序法优化:当我们要排序的数组本身就是有序的时候,或者说数组排序的时候排好前几项就已经有序了,这时候按照上面的方法还要进行两两比较,效率就比较低,对此我们进行想要的优化。

#include
void print(int arr[], int sz)
{
     
	int i = 0;
	for (i = 0; i < sz; i++)
	{
     
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void bubble_sort(int arr[], int sz)
{
     
	int i = 0;
	int j = 0;

	for (i = 0; i < sz - 1; i++)//确定冒泡排序的趟数
	{
     
		int flag = 1;//用于判断比较是否继续
		for (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;
				flag = 0;
			}
		}
		if (flag == 1)
		{
     
			break;
		}
	}
}
int main()
{
     
	int arr[] = {
      9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前:");
	print(arr, sz);
	//冒泡排序,进行升序排列
	bubble_sort(arr, sz);
	printf("排序后:");
	print(arr, sz);
	return 0;
}

思考:数组名到底是什么?

我们经常会说数组名是首元素地址,那么这个说法对不对呢?这里我们可以验证一下。
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第22张图片
可以看到两者一样,所以数组名是首元素地址。

但是要排除以下两种情况:
①sizeof(数组名)-- - 数组名表示整个数组-- - 计算的是整个数组的大小,单位是字节
② &数组名-- - 数组名表示整个数组-- - 取出的是整个数组的地址
除上述两种情况以外,其余情况数组名均表示首元素地址!


数组地址和数组首元素地址有什么区别?

两者的地址值是一样的,但是含义和使用不同。
数组地址 + 1表示跳过整个数组,首元素地址 + 1表示跳到第二个元素。
【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第23张图片

& arr + 1 与 & arr 相差40个字节
arr + 1 与 arr相差4个字节


三维数组及多维数组

三维数组具有高、宽、深的概念,或者说行、列、层的概念,即数组嵌套数组达到三维及其以上。是最常见的多维数组,由于其可以用来描述三维空间中的位置或状态而被广泛使用。
三维数组就是维度为三的数组,可以认为表示对该数组存储的内容使用了三个独立参量去描述,但更多的是认为该数组的下标是由三个不同的参量组成的。三维数组又被认为是二维数组的数组,而二维数组也可以认为是一维数组的数组。
数组这一概念主要用在编写程序当中,和数学中的向量、矩阵等概念有一定的差别,主要表现在数组内的元素可以是任意的相同数据类型,包括向量和矩阵。
对数组的访问一般是通过下标进行的。在三维数组中,数组的下标是由三个数字构成的,通过这三个数字组成的下标对数组的内容进行访问。
多维数组:三维或者三维以上的数组。
定义方式:type name[size1][size2]…[sizeN];
例如,下面的声明创建了一个三维 4 . 3 . 2 整型数组:
int threedim[4][3][2];

【C语言基础学习笔记】四、数组详解(超详细!超硬核!)_第24张图片
就目前学习而言,很少使用到三维及以上数组形式,所以就不在进行进一步的研究和说明,对其有个大概的了解和认识即可!

你可能感兴趣的:(C语言编程学习,数组,c语言)