欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C 语言综合篇]数组 - 从不知到知

最编程 2024-05-05 18:47:15
...

数组

【C语言总集篇】数组篇——从不会到会的过程_初始化
【C语言总集篇】数组篇——从不会到会的过程_初始化

前言

大家好,很高兴又和大家见面啦!前面咱们已经把函数的相关知识点学习完了,今天咱们将开始进入数组内容的学习。在本篇章中,我会给大家带来一维数组与二维数组的详细内容,接下来我们就开始今天的正题吧!

一维数组的创建和初始化

1.数组的创建

1.1数组定义

数组是一组相同类型元素的集合

我们怎么来理解这个相同类型呢?这里我们可以借用数学的角度来理解;

在数学中,集合就是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成集合的这些对象则称为该集合的元素。

这里我们可以简单的举几个例子来说明集合:

  • 自然数的集合

就是由

这些非负整数组成的一个集体;

  • 正整数的集合

就是由

这些正整数组成的一个集体;

  • 整数的集合

就是由

这些整数组成的一个集体;

  • ……

这些集合里的元素都是满足了某一特定的条件,在计算机中,这一特定的条件就是相同的类型。 这里的类型指的就是元素的数据类型。所以我们需要有一个概念就是——

只有相同类型的元素才能组成一个集合

在计算机语言中,我们把这种集合称为数组。 比如int类型的元素只能与int类型的元素组成集合称为整型数组; char类型的元素只能与char类型的元素组成集合称为字符数组……

现在我们知道了什么是数组了,那我们如何用计算机语言来创建一个数组呢?下面我们就来介绍一下数组的创建方式;

1.2数组的创建方式

对于一个数组的创建,我们需要有3个要素——数组元素类型、数组名以及数组的空间大小或者说是数组的元素个数。数组创建的格式如下所示:

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

按照这个格式,我们就可以尝试着创建出字符数组、整型数组、浮点型数组……

//字符数组
char ch[10];
//整型数组
short / int / long / long long arr[20];
//浮点型数组
float / double arr[30];

在这个格式中我们需要注意几个点:

  • 数组的元素类型包括但不限于上述提到的这些原子类型,数组元素的数据类型还可以是像结构体这样的自定义类型;
  • 数组名与变量名是一样的,没有强制性的要求一定是要使用什么名字,可以根据个人喜好来命名;
  • const_n表示的是常量表达式,如果我们使用的是变量的话,那数组是无法成功创建的;
  • 当数组的大小确定后,我们是不能对数组的大小进行更改的。

在C99之后引入了变长数组的概念,允许我们可以使用变量指定数组大小。也就是说在支持C99标准的编译器比如gcc编译器是可以在创建数组时使用变量来表示数组大小的;

博主使用的编译器是VS2019,这个编译器下是不支持变长数组的,但是我们在创建数组时除了上述的这种按格式创建数组外,还可以像下面这种格式来创建数组:

type_t arr_name[] = { array_element };
//type_t——数组的元素类型;
//arr_name——数组的名字;
//array_element——数组元素;

通过这种格式创建的数组,其数组的大小与数组的元素个数是相等的,数组的大小确定后同样也是不可以更改的。

如下所示:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_08
【C语言总集篇】数组篇——从不会到会的过程_十六进制_08

从这个监视窗口中我们可以看到,此时数组内的6个元素都已经完成的赋值,但是在进行第7个元素的赋值后系统报错了报错内容是数组arr的栈区损坏了。也就是说,通过这种格式创建的数组并不是代表它的大小可以被改变,数组的大小与数组定义时的元素各数是相同的。

这种省略数组大小的格式我们可以理解为是是以元素的个数表示数组大小。这种形式其实是直接在创建数组时就给数组进行初始化。下面我们就来探讨一下什么是数组的初始化;

2.数组的初识化

2.1数组初始化定义

创建数组的同时给数组的内容一些合理初始值

2.2数组初始化的形式

我们在对数组进行初始化时有两种形式——完全初始化和不完全初始化。

//数组的初始化
//整型数组
int main()
{
	int arr1[5] = { 1,2,3,4,5 };//完全初始化
	int arr2[] = { 6,7,8,9,10 };//完全初始化
	int arr3[5] = { 11,12,13 };//不完全初始化
	return 0;
}

在这个代码中我们可以看到,通过第一种格式创建的数组是可以进行完全初始化和不完全初始化的,但是对于第二种省略数组大小的创建格式来说,它就只有完全初始化这一种方式。

前面我们是以整型数组来举例的,下面我们再来看一个代码:

//字符数组
int main()
{
	char ch1[] = "abcd";
	char ch2[] = { 'a','b','c','d' };
	return 0;
}

大家思考一下,这两个字符数组一样吗?为了搞清楚这个问题,接下来我们来介绍一下数组内的元素;

2.3数组的元素

对于整型数组来说,我们可以很容易理解,数组内的元素就是对应类型的数字,但是对于字符数组来说,它的元素是有两种书写形式的:

  • 一种是和整型数组一样的,使用单引号将字符一个一个的放入数组的大括号"{}"中;
  • 还有一种形式就是通过双引号将多个字符直接赋值给数组。

这种通过双引号引起的一个或多个字符被称为字符串

那这字符串和单个字符又有什么区别呢?下面我们来探讨一下它们之间的区别。这里我们需要引入一个库函数——计算字符串长度的库函数——strlen。

它的作用就是通过计算字符的个数来计算字符串的长度,我们在使用时需要引用头文件string.h。 下面我们就来看一下这两个数组的区别;

【C语言总集篇】数组篇——从不会到会的过程_初始化_09
【C语言总集篇】数组篇——从不会到会的过程_初始化_09

从这个结果中我们可以看到,不仅是计算的字符串长度不同,而且打印出来的字符串也是不一样。 为什么会有这种区别呢?下面我们来进行调试通过监视窗口观察一下:

调试的步骤:

【C语言总集篇】数组篇——从不会到会的过程_数组_11
【C语言总集篇】数组篇——从不会到会的过程_数组_11

我们在打开监视窗口后在输入栏输入我们想要观察的对象就可以了,接下来我们就可以通过监视窗口观察两个数组的元素了:

【C语言总集篇】数组篇——从不会到会的过程_数组_12
【C语言总集篇】数组篇——从不会到会的过程_数组_12

在监视窗口中,我们可以得到以下几个信息:

  • ch1/ch2这两个数组里面存放的内容是不相同的
  • 数组ch1中存放了五个元素——字符a、b、c、d和字符\0;
  • 数组ch2中存放了四个元素——字符a、b、c、d;
  • 在系统错误列表窗口中有一个警告信息——字符串ch2没有添加字符串零终止符;
  • 报错的代码是在strlen这一行;

由以上信息我们可以做个猜想,是不是只要我们在ch2中加入这个\0,那这两个数组存放的内容就一致了呢?下面我们就来实践一下:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_13
【C语言总集篇】数组篇——从不会到会的过程_十六进制_13

可以看到此时我们打印的内容就完全一样了,现在我们就可以得到结论:

  • 由双引号引起的字符串是由看的到的字符与看不到的\0组成的
  • \0是字符串的终止符

那这个\0是怎么来的呢?如果此时我们只有一个双引号"",朋友们你们说它里面有没有字符呢?这就是我们要介绍的一个新的知识点——空字符串;为了探讨一下这个空字符串里面的元素,下面我们可以通过监视窗口来观察一下这个空字符串:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_14
【C语言总集篇】数组篇——从不会到会的过程_十六进制_14

可以看到在这个ch数组中是有元素的,而且只有一个元素,这个元素就是\0; 也就是说双引号其实是自带一个\0的。

现在对于字符串和字符我相信大家都已经了解了,下面我们再来了看一个代码:

//字符数组
#include <string.h>
int main()
{
	char ch1[5] = "abcd";
	char ch2[5] = { 'a','b','c','d'};
	//通过strlen来计算数组的字符串长度
	printf("%d  %d\n", strlen(ch1), strlen(ch2));
	//%s——以字符串的格式进行打印
	//将数组的字符以字符串的形式输出
	printf("%s  %s\n", ch1, ch2);
	return 0;
}

这两个数组存放的元素有没有区别呢?下面我们来看一下运行结果:

【C语言总集篇】数组篇——从不会到会的过程_数组_15
【C语言总集篇】数组篇——从不会到会的过程_数组_15

从运行结果中我们可以看到,两个数组的打印内容完全一致,为什么会这样呢?下面我们就来介绍一下完全初始化和不完全初始化的区别;

2.3.1完全初初始化和不完全初始化的区别

现在我们对这个代码直接通过监视窗口来查看这两个数组里的元素:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_16
【C语言总集篇】数组篇——从不会到会的过程_十六进制_16

从监视窗口中我们可以看到此时的两个数组存储的元素是一致的,都是5个元素而且都是由字符a/b/c/d和\0组成的。 那现在问题就来了,我们在数组ch2中只给4个元素进行了初始化呀,为什么第五个元素变成了\0? 为了解开这一疑惑,我们再来看一组代码:

//不完全初始化
int main()
{
	char ch1[5] = { 'a' };
	char ch2[5] = { 'a','b' };
	char ch3[5] = { 'a','b','c' };
	char ch4[5] = { 'a','b','c','d'};
	return 0;
}

在这个代码中,我们对四个数组都进行了不完全初始化,下面我们来通过监视窗口看一下数组里的元素的情况:

【C语言总集篇】数组篇——从不会到会的过程_数组_17
【C语言总集篇】数组篇——从不会到会的过程_数组_17

从监视窗口中我们可以看到,这四个数组中未被初始化的元素都变成了\0,那是不是只有字符数组这样呢?下面我们来测试一下整型数组和浮点型数组:

【C语言总集篇】数组篇——从不会到会的过程_初始化_18
【C语言总集篇】数组篇——从不会到会的过程_初始化_18

此时的数组并未进行初始化,我们可以看到数组里的内容都是随机值,下面我们给数组进行初始化再看看:

【C语言总集篇】数组篇——从不会到会的过程_初始化_19
【C语言总集篇】数组篇——从不会到会的过程_初始化_19

现在我们已经完成了这三个数组的不完全初始化,此时我们可以看到不管是字符数组还是整型数组亦或是浮点型数组除了首元素被初始化为确定值以外,其它的元素同时也被初始化为0; 也就是说所谓的不完全初始化其实也是完全初始化,只不过初始化的方式与完全初识化不一样,完全初始化是每个元素都被赋予了一个确定的值,而不完全初始化未被赋予确定值的元素会被赋予0; 那现在我们就可以得到结论:

  • 完全初始化中,每个元素都会被主动赋予一个确定的值进行初始化;
  • 不完全初始化中,未被赋予值的元素会被赋值为0进行初始化;

现在我们已经完成了数组的创建和初始化的过程,接下来我们就要开始对数组进行使用了;

3.一维数组的使用

对于数组的使用,我们需要介绍一个操作符:[]——下标引用操作符。它其实就是数组访问数组元素的操作符。通过这个操作符,我们对数组就有两种使用方式:通过数组下标访问数组元素、通过数组下标计算数组大小。

3.1通过数组下标访问数组元素

在介绍通过下标来访问数组元素之前我们先需要了解什么是数组下标;

3.1.1数组下标

下面我们还是通过代码来介绍数组的下标:

【C语言总集篇】数组篇——从不会到会的过程_初始化_20
【C语言总集篇】数组篇——从不会到会的过程_初始化_20

从监视窗口中我们可以得到以下的信息:

  • []中间的数字就是数组的下标;
  • 这个下标是从0开始依次递增的;
  • 每一个元素都对应一个下标;
  • 元素的下标与元素个数的差值为1;

现在我们知道下标是什么了,那我们如何来通过下标来访问元素呢?

3.1.2访问格式

在使用数组下标来访问数组元素的格式很简单:

//访问格式
//数组名+下标引用操作符[]+元素下标
//书写形式
//数组名[元素下标]
//如:arr[0]/arr[1]/arr[2]……

下面我们来尝试着通过下标来将数组的元素打印在屏幕上:

【C语言总集篇】数组篇——从不会到会的过程_数组_21
【C语言总集篇】数组篇——从不会到会的过程_数组_21

可以看到我们现在确实可以通过下标来访问数组的各个元素。

这里我们需要注意一件事:!!!通过下标引用操作符访问数组元素和数组定义是两码事

下面我们来看一下它们的异同点;

3.1.3访问数组元素和定义数组的异同点

访问数组元素和定义数组有以下几个异同点,我们借助代码来进行说明:

int arr[5] = { 1,2,3,4,5 };
	arr[0];
	arr[1];
	arr[2];
	arr[3];
	arr[4];
相同点:

  • 数组名相同——都是arr;
  • 括号相同——都是[]
不同点

  • 定义数组时有数组元素的类型,通过下标访问数组元素时,没有元素类型;
  • 定义数组时中括号里的数字代表的是数组大小,通过下标访问数组元素时,中括号里的数字代表的是元素下标;
  • 数组的大小比元素最大的下标多1;

所以大家要清楚对于int arr[4];arr[4];这是两个意思,前一个是在创建数组,后一个是在通过数组下标引用数组元素;

现在我们知道了通过下标可以访问数组元素,接下来我们来介绍第二种用法——通过数组下标计算数组大小;

3.2通过数组下标计算数组大小

看到这个作用,可能就会有朋友奇怪了,为什么我们需要通过数组下标来计算数组的大小呢?我们在创建数组时中括号的数组不就代表着数组大小吗?别着急,下面我们来看一下这个代码:

//通过数组下标计算数组大小
int main()
{
	char ch[] = "asdfsandgjinwengasdhfasflsdannsiadnf";
	return 0;
}

对于这个数组,大家能第一时间说出它的大小吗?这只是一个例子,可能之后我们还会碰到元素更多的数组,到那时我们想要计算数组的大小不可能说一个元素一个元素的去数吧,这里就需要我们借助下标来计算数组的大小了。

那我们应该如何计算呢?下面我们需要介绍一个C语言的操作符——sizeof;

sizeof是用来计算操作数所占内存空间大小的操作符,单位是字节。这里的操作数可以是变量、数据类型也可以是数组。它的使用格式也很简单——sizeof(操作数);

接下来我们就来尝试着借助操作数来计算数组的大小以及数组元素的大小;

【C语言总集篇】数组篇——从不会到会的过程_初始化_22
【C语言总集篇】数组篇——从不会到会的过程_初始化_22

从这个测试结果中我们可以看到,数组ch所占内存空间大小为37,ch的首元素所占空间大小为1;数组arr所占内存空间大小为84,arr的首元素所占空间大小为4;

我们在使用sizeof这个操作符计算数组所占空间大小时,它其实是求的数组中所有元素所占空间的总大小

也就是说,如果我有了数组中所有元素所占空间的总大小以及一个元素所占空间大小时,我只需要用总大小除以一个元素所占空间大小是不是就能得到元素的总个数也就是数组的大小了呢?下面我们就来测试一下:

//一维数组的使用
//通过数组下标计算数组大小
int main()
{
	char ch[] = "asdfsandgjinwengasdhfasflsdannsiadnf";
	int sz1 = sizeof(ch);//整个数组所占内存空间大小
	int sz2 = sizeof(ch[0]);//数组首元素所占内存空间大小
	printf("sz1 = %d\n", sz1);
	printf("sz2 = %d\n", sz2);
	printf("sz_ch = %d\n", sz1/sz2);
	int arr[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,11,1,1 };
	int sz3 = sizeof(arr);//整个数组所占内存空间大小
	int sz4 = sizeof(arr[0]);//数组首元素所占内存空间大小
	printf("sz3 = %d\n", sz3);
	printf("sz4 = %d\n", sz4);
	printf("sz_arr = %d\n", sz3 / sz4);
	int test[5] = { 1,2,3,4,5 };
	int sz5 = sizeof(test);//整个数组所占内存空间大小
	int sz6 = sizeof(test[0]);//数组首元素所占内存空间大小
	printf("sz5 = %d\n", sz5);
	printf("sz6 = %d\n", sz6);
	printf("sz_test = %d\n", sz5 / sz6);
	return 0;
}

在这个代码中我们通过数组test来验证咱们答案的准确性。对于数组test,我们可以看到,它是一个大小为5的整型数组,如果计算的结果也是5,那就说明我们的想法是正确的。下面我们来看一下运行结果;

【C语言总集篇】数组篇——从不会到会的过程_数组_23
【C语言总集篇】数组篇——从不会到会的过程_数组_23

从测试结果中可以看到我们确实可以通过将数组所占空间大小除以数组元素所占空间大小从而来计算数组的大小。那我们现在可以出结论:

这里的数组元素可以是数组中的任一元素,之所以取首元素是因为一个数组肯定会有下标为0的首元素,但不一定会有下标为其它的元素。

如:现在有一个数组int arr[]={1,2,3,4,5};这个数组中如果我们要计算它的大小我们可以使用下标0、1、2、3、4这五个下标中的任意一个;通过下标引用操作符来访问也就是arr[0]arr[1]arr[2]arr[3]arr[4]这五个元素的任意一个;但是不能使用arr[5];因为在这个数组中并没有下标为5的元素为了避免出错,建议大家在计算数组大小时尽量使用arr[0]

现在我们已经介绍完了数组的创建、初始化以及使用,接下来我们来看看数组在内存中是如何存储的;

4.一维数组在内存中的存储

我们要介绍数组在内存中的存储之前,我们需要先了解一下内存的相关知识点;

4.1计算机存储数据的方式和关键字register:

计算机要存储数据的话有以下几种途径,按访问速度由快到慢来排列分别是:

它们的存储空间大小是依次增大的,寄存器的存储空间大小最小硬盘存储空间大小最大

计算机在进行运算时是先从内存中提取数据然后再将数据输送到*处理器(CPU)进行运算。刚开始的时候内存的读取速度和CPU处理数据的速度是非常匹配的,但是随着时代的发展,CPU的处理速度越来越快,快到内存的访问速度跟不上了,后面为了解决这个问题,就开始出现了有更快访问速度的高速缓存和比高速缓存访问速度更快的寄存器。在这之后CPU在处理数据时会先在寄存器里拿取需要处理的数据,如果没有,再去访问高速缓存,还是没有,再去访问内存,像这样的一个访问流程大大提高了计算机的运行速度。寄存器中的数据又是怎么来的呢?它是先由内存里的数据下载到高速缓存,再由高速缓存下载到寄存器这样一个流程。这里就需要提到register——寄存器关键字。这个关键字是干嘛用的呢?你可以理解为它就是将数据下载到寄存器里的通道;比如我想定义一个变量int a = 10;在后面的代码中我需要多次使用它,为了更快的读取这个数据,我就可以将它定义为寄存器变量——register int a = 10;但是有一个问题,前面也提到了寄存器是空间最小的,如果我把所有内容都放到寄存器里面,它也装不下呀,那怎么办呢?这里我们就要知道register这个通道它不是一路通畅的,它是会经过筛选的,它会筛选出真正需要的数据,它筛选的途径又是什么呢?它筛选的途径就是咱们使用的编译器,我们在定义寄存器变量后,这些被定义的变量就像是拿到一张入场券一样,它们要先到编译器的面前接受审核,审核通过了,才能进入寄存器。所以我们又可以将register定义的变量称为建议将其定义成寄存器变量

上面的内容我们只需要了解计算机有寄存器、高数缓存、内存、硬盘这四种存储方式和register整个寄存器关键字的作用就行了,不需要去深究,这里我就不多说了。

4.2内存

通过前面的内容我们了解到了下面几点:

  • *处理器(CPU)在读取数据时是从寄存器—>高速缓存—>内存这个顺序来读取的,
  • 寄存器中的数据是由内存—>高速缓存—>寄存器这个顺序来存放的。
  • 内存是电脑上特别重要存储器,计算机中所有的程序的运行都是在内存中进行的。

这里我们可以知道,内存它是一个空间,它是一个可以存放和读取数据的空间

我们可以将内存想象成冰箱,里面可以存放各种各样的东西。我们为了更加高效的使用冰箱,我们就将冰箱分成了不同的区块——有冷藏区和冷冻区;冷藏区又会根据自己的喜好不同分成不同的小分区,有存放蔬菜的,有存放水果的,有存放饮料的,有存放零食的等等;为了快速的找到这些分区我们会给他们进行取名,或者是编号。比如存放蔬菜的我把它叫做蔬菜区或者把它叫做1号,存放水果的我把它叫做水果区或者把它叫做2号……我们下次在使用这些分区时只需要找到它们对应的名称或者编号是不是就可以了。

在我们的内存中也同样如此: 内存被划分成了一个个小的内存单元,每个内存单元都有它相应的编号,这些编号我们称为内存单元的地址

大家再思考一个问题,我们在给冰箱分区的时候是不是每个空间都会有一定的大小啊,那在内存中一个内存单元又有多大呢?这里我们直接记结论——每个内存单元的大小是1个字节

4.3.地址的表现形式

既然在内存中,每一个内存单元都有自己的地址,那地址的表现形式是什么呢?我们可以通过%p——以地址的格式进行打印和操作符&——取地址操作符来看一下地址的表现形式,如图所示:

【C语言总集篇】数组篇——从不会到会的过程_初始化_27
【C语言总集篇】数组篇——从不会到会的过程_初始化_27

我们可以看到,这个地址打印出来,又是数字又是字母的,这是什么东西呢? 其实这种表现形式是通过16进制来表现的。那什么是十六进制呢?下面我们就来介绍一下;

4.4.十六进制

十六进制是在十进制的基础上加上了字母

,这些字母分别代表

;十进制是逢十进一,而十六进制则是逢十六进一。十六进制的数按从小到大排列分别是:

// 十六进制
十六进制数:0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  十进制数:0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

计算机在存储数据的时候,肯定不是按十六进制来存储的,计算机存储数据只能通过二进制来存储,但是在向我们展示的时候则是通过十六进制来进行展示,那这个十六进制转化为二进制又是多少呢?我们可以借助程序员计算器来看一下:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_30
【C语言总集篇】数组篇——从不会到会的过程_十六进制_30

可以看到,这个十六进制的数转化为二进制时是:1000 1111 1111 1010 1001 1100。这时候有朋友就会问了,地址在32位系统中不应该是32个比特位吗?为什么这里只有24位,还有八位呢?这个问题问的非常好,剩下的八位去哪了呢?我们不妨再仔细看一下打印出来的地址前面两位是什么?它完整的地址应该是008FFA9C,这里每个数值对应4个比特位,前面我们才提到十六进制的每一个数对应的十进制的数分别是什么,下面我们就来将其转换成二进制来看看:

十六进制    十进制   二进制
 	  0					0				0000
		0					0				0000
		8					8			  1000
		F					15			1111
		F					15			1111
		A					10			1010
		9					9				1001
		C					12			1100

现在我们再跟刚刚转换的值对比一下,有没有发现是不是漏掉了两个0所对应的二进制数值呀,所以这里完整的二进制位应该是:

0000 0000 1000 1111 1111 1010 1001 1100

有朋友就会问了,这个十六进制、十进制、二进制它们究竟是如何转化的呢?我们总不可能手上都带着电脑吧,没有带电脑手机的时候我们应该如何进行进制之间的转化呢?下面我们就来介绍一下进制的转化过程;

4.5进制之间的相互转化
4.5.1二进制与十进制的相互转化

二进制转化成十进制,这里我拿1110来举例:

我们可以看到这里的每一位都代表着2的n次方,从右往左依次是0/1/2/3……二进制位越多,那2的次方也就越多,32个二进制位也就是最高位是231;

十进制转化成二进制,这里我们还是用14来举例:

大家通过这个计算有没有发现什么? 14对应的二进制位从右到左是不是通过除以2取的余数呀,第一次除以2的余数就是二进制位的第一位,第一次除以2的余数就是二进制位的第二位…… 以此类推,第几次除以2所得的余数就是第几位的二进制位; 下面我们可以得到结论:

4.5.2十进制与十六进制相互转化

十六进制转化成十进制,这里我拿9C来举例:

【C语言总集篇】数组篇——从不会到会的过程_数组_39
【C语言总集篇】数组篇——从不会到会的过程_数组_39

我们可以看到,十六进制转十进制和二进制转十进制是一样的,只不过二进制是2的n-1次方,十六进制则是16的n-1次方;

十进制转化成十六进制也是一样,我们同样还是拿156来举例:

12对于的十六进制的数是C,所以最后我们可以得到的十六进制数就是9C; 下面我们可以得到结论:

我相信现在大家对十进制、十六进制、二进制的相互转化应该是没什么问题了。接下来我们继续介绍数组及数组元素的地址;

4.数组及数组元素的地址

我们通过下面的代码来看一下一维数组是如何在内存中存储的:

//一维数组在内存中的存储
int main()
{
	char a[] = "abc";
	int sz1 = sizeof(a) / sizeof(a[0]);//数组a的大小
	printf("&a=%p\n", &a);//数组a的地址
	for (int x = 0; x < sz1; x++)
	{
		printf("&a[%d}=%p\n", x, &a[x]);//打印数组a各元素的地址
	}
	short b[] = { 1,2,3,4 };
	int sz2 = sizeof(b) / sizeof(b[0]);
	printf("\n&b=%p\n", &b);
	for (int y = 0; y < sz2; y++)
	{
		printf("&b[%d}=%p\n", y, &b[y]);
	}
	int c[] = { 1,2,3,4 };
	int sz3 = sizeof(c) / sizeof(c[0]);
	printf("\n&c=%p\n", &c);
	for (int z = 0; z < sz3; z++)
	{
		printf("&c[%d}=%p\n", z, &c[z]);
	}
	return 0;
}

这里我分别定义了字符数组a、短整型数组b、和整型数组c,我们既然要了解数组在内存中的存储,那我们就需要知道它们在内存中的地址,我们通过数组的地址与数组元素的地址来说明它们在内存中是如何存储的:

【C语言总集篇】数组篇——从不会到会的过程_数组_44
【C语言总集篇】数组篇——从不会到会的过程_数组_44

从这个打印结果我们可以看到数组的地址与数组第一个元素的地址相同, 在char类型的数组中,元素的地址相差1; 在short类型的数组中,元素的地址相差2; 在int类型的地址中,元素的地址相差4。 下面我们来看一下这些数据类型在内存中所占空间大小:

【C语言总集篇】数组篇——从不会到会的过程_十六进制_45
【C语言总集篇】数组篇——从不会到会的过程_十六进制_45

可以看到,在内存中不同类型所占空间大小如下:

char类型在内存中所占空间大小刚好是1个字节;short类型所占空间大小是2字节;int类型所占空间大小是4字节;

这样一对比有没有发现什么呀?数组元素的地址之间的差值与元素的数据类型所占空间大小相等。也就是说不管是字符数组还是整型数组,数组元素的地址都是紧挨着的,而且数组元素的地址是从低地址到高地址进行连续存放的。现在我们就可以得出以下结论:

  • 数组的地址与数组中首元素的地址相同;
  • 每个元素地址相差的字节大小与元素的类型所占空间大小一致;
  • 数组在内存中是由低地址到高地址连续存放的;

现在我们就介绍完了一维数组的全部内容,接下来我们来介绍一下二维数组;

二维数组的创建和初始化

1.二维数组的创建

1.1什么是二维数组
1.1.1个人理解

对于二维数组,我是这样理解的:一维就是一条线,二维就是一个面,那一维数组就是只有一行或者一列的数组,而二维数组则是拥有行和列的数组。

1.2二维数组如何创建

既然我们理解的二维数组具有行和列,那我们就需要有两个下标来进行表示,如:

//二维数组的创建
char a[1][2];
short b[1][2];
int c[1][2];

和一维数组的创建方式一样,有数组元素类型、数组名、以及数组的大小,但是这里的大小由两部分组成,这里我们理解的是行与列两部分,具体是不是呢?我们接着往下看;

2.二维数组的初始化

2.1初始化的分类

和一维数组一样,二维数组同样也是分为两种初始化:完全初始化和不完全初始化。

2.1.1完全初始化

数组初始化的元素个数与数组大小相同;

2.1.2不完全初始化

数组初始化的元素个数小于数组大小,未被初始化的元素默认为0;

2.2二维数组初始化

在一维数组中,我们知道了初始化就是在创建数组时给数组的内容一些合理初识值,那二维数组又应该怎样赋值呢?我们通过代码来说明二维数组的初识化:

【C语言总集篇】数组篇——从不会到会的过程_初始化_46
【C语言总集篇】数组篇——从不会到会的过程_初始化_46

在代码中我们先定义了一个二行三列的二维数组,随即就给它赋值了4个元素,从调试中我们可以看到, 各个元素的下标分别是:

;对应的元素名称为:

有没有一种熟悉感,是不是和线性代数学的行列式很相似啊,既然这样那我们是不是可以把这个数组的元素用图像表示出来呢?

【C语言总集篇】数组篇——从不会到会的过程_数组_49
【C语言总集篇】数组篇——从不会到会的过程_数组_49

这里我们可以总结一下几点内容:

  • 二维数组的下标也是从0开始,二维数组的首元素下标为[0][0],然后从第二个下标开始依次增加;
  • 二维数组的元素个数为两下标的乘积,如a[2][3]这个二维数组的元素个数有

个;

  • 二维数组在初始化时,和一维数组一样会从首元素开始初始化,未被初始化的元素,会默认被0初始化。

那我能不能把1、2赋值给第一行的两个元素,把3、4赋值给第二行的两个元素呢?答案是可以的。如图所示:

【C语言总集篇】数组篇——从不会到会的过程_数组_51
【C语言总集篇】数组篇——从不会到会的过程_数组_51

这里的初始化方式我是这么理解的,既然二维数组分行和列的话,通过元素的下标我们可以将行相同的元素看做一个整体,或者说看做一个一维数组也就是int a0[3];int a1[3];两个数组;那我的二维数组我就可以写成

int a[2][3]={a0[3], a1[3]};
//int——数组元素类型;
//a——二维数组名;
//2——一维数组的个数;
//3——一维数组的元素个数;
//a0——数组首元素数组名;
//a1——数组第二个元素数组名;

现在我们再来给这个数组初始化的话是不是就相当于分别给a0[3]a1[3]这两个数组初始化呢?所以我们只需要把需要赋给它们的初始值用大括号括起来就OK了。也就是:

a0[3]={1, 2};
a1[3]={3, 4};
int a[2][3]={{1, 2}, {3, 4}};

接下来有朋友可能就会提问了,在一维数组中我们有提到过可以省略数组大小直接给数组进行初始化的方式,也就是在创建数组时,不给定数组大小,只给定数组初始化的元素,如a[]={1,2,3,4,5,6}。那在二维数组中有没有这种创建数组的方式呢?下面我们来测试一下,分别从省略行和列、省略行、省略列来进行探讨:

【C语言总集篇】数组篇——从不会到会的过程_初始化_52
【C语言总集篇】数组篇——从不会到会的过程_初始化_52

在省略行和列时,系统会报错说明a缺少下标,并在第二个中括号下面标注了一下;

【C语言总集篇】数组篇——从不会到会的过程_十六进制_53
【C语言总集篇】数组篇——从不会到会的过程_十六进制_53

我们在省略行时,代码成功编译,并且根据列的大小将元素划分成了两组;

【C语言总集篇】数组篇——从不会到会的过程_十六进制_54
【C语言总集篇】数组篇——从不会到会的过程_十六进制_54

我们在省略列时,系统再次报错,这一次报错了两个内容,一个缺少下标,一个初始值设定项值太多,并在代码的第二个中括号和元素的第四个元素下做了标注。 从上面的结果,我们可以得出以下结论:

  • 二维数组在创建时