c语言入门--数组

前言

**

啊哈亲爱的小伙伴们大家好啊,我们又见面了,看了函数栈帧这篇文章是不是很有点感触啊,那么我们函数这一章节就这么结束,我们接下来就开始学习数组了,废话不多说我们开始吧。

**

一.为什么会有数组

  人们通常借助计算机完成统计每月的支出,日降雨量,季度销售额等任务。企业借助计算机管理薪资,库存和客户的交易记录等,作为程序不可避免的要处理大量的相关的数据。那么为了能够高效的处理这些相关相似的数据,我们c语言就有了数组这个东西,来帮助我们高效的处理这些相关的数据。

二.一维数组

1.一维数组的创建

  大家首先先回想一下,我们在创建变量的时候我们得告诉电脑我们这个变量的类型是什么,然后还得告诉电脑这个变量的名字是什么,那么我们在创建数组的时候也是同样的道理,但是我们数组是由数据类型相同的一系列元素组成的,我们通过一系列这个词可以知道一件事情就是:我们在创建数组的时候也得告诉电脑我们这个数组有多少个元素,那么看到这里我们就知道了在创建数组的时候要满足三个点就是:

1. 创建数组的时候要有具体的数组名,这个数组名的命名规则跟我们创建变量时候的规则一模一样。
2. 创建数组的时候得在数组名前面写上数据的类型,比如说int,float之类的。
3. 创建数组的时候我们得在数组名后面的方括号里面加上数组元素的个数,当然这里是创建数组的时候要加,如果是创建数组并且对其进行初始化的话,可以根据情况不加,我们下面会讲到。

那么看到这里我们来举几个创建数组的例子:

#include<stdio.h>
int main()
{
	int arr1[10];//int 类型的数组 元素个数为10个
	char arr2[20];//char 类型的数组 元素个数为20个
	float arr3[15];//float 类型的数组元素个数为15个
	return 0;
}

这里还有几点要告诉大家就是,

1.上面创建数组过程中的[ ]其实是有一定作用的,[ ]除了作为操作符可以使用数组中的元素外,还可以表明arr1,arr2,arr3是一个数组。
2. 我们说在创建数组的时候得在括号里面填入数据来告诉计算器这个数组的大小是什么,那么这时候可能有有小伙伴们要问了,这个数据的类型是什么呢?他可以是字符类型吗?它可以是浮点型吗?它可以是一格变量吗?他可以是一串表达式吗?那么这里我们要告诉大家的是我们这个[ ]里面必须得是整型常数表达式,并且表达式的值必须得大于0。
那么我们再回头来看这么几个问题:

#include<stdio.h>
int main()
{
	int n = 5;
	float a1[5];
	float a2[5*2+1];
	float a3[sizeof(int)+1];
	float a4[-4];
	float a5[0];
	float a6[2.5];
	float a7[(int)2.5];
	float a8[n];
	float a9['a'];
	return 0;
}

1. 第一个数组a1的[ ]里面直接是5所以这个数组创建的过程中没有什么问题。
2. 第二个数组a2的[ ]里面是是一个表达式5*2+1,虽然是一个表达式但是表达式的结果是11大于0,所以也成立没有问题。
3. 第三个数组a3的括号里面有一个操作符sizeof,但是他始终还是一个表达式并且表达式的结果是5(在32位的情况下大于0,所以没有问题。
4. 第四个数组a4的[ ]里面是-4为负数所以这里就直接违背表达式大于0的规则所以这里的数组创建的规则。
5. 第五个数组a5的[ ]里面为0虽然不是负数,但是他等于0啊,我们这里要求的是大于0,所以也不成立。
6. 第六个数组a6的[ ]里面是2.5虽然他的值大于0,但是我们要求的数组的大小必须得是整数,这里为浮点型的数据,所以不成立。
7. 第7个数组a7的[ ]里面是2.5但是前面有操作符将其转化为int类型就是整型所以我们这里的数组的创建没有问题
8. 第8个数组a8的[ ]里面是一个变量n,那么如果是这样的话情况就稍微复杂一点因为在c99标准之前,数组的[ ]里面必须得是一个常量才可以,不能是一个变量,但是在c99标准中又规定了可以使用变量,我们把这些使用变量的数组称为边长数组,但是这些边长数组不能进行初始化。

9. 第九个数组的[ ]里面的是一个字符a,看到这个很多小伙伴们可能会觉得这里错了,但是大家要记住一件事情就是字符a在我们计算机中的存储的也是他的Ascall码值实际上还是一个整型的常量并且大于0,所以这个创建并没有什么问题。

看到这里想必大家对这个数组的创建有了很深的理解那么接下来我们就来看看一维数组的初始化部分。

2.一维数组的初始化

为什么要初始化?

  我们可以把这个与我们创建变量的情况联系在一起,我们之前说过我们在创建变量的时候如果不对其进行初始化的话,这个变量里面装的都是一些没用的垃圾数据,所以我们要对他进行初始化,看了前面的函数栈帧我们知道这个垃圾数据就是cccccccc,那么我们数组也是一样的,如果不对其初始化的话里面的数据就是cccccccc了,但是有小伙伴们可能要说了,我可以通过赋值来改变我的数组的内容啊,但是大家有没有想过你要是能把这个数组中的全部元素都赋值了,那还好说,如果没有赋值到,那是不是就是一个垃圾数据了啊,要是一下子不小心使用到他了,那得多难看啊对吧,那么我们这里说的初始化其实是包括了创建数组的过程,没必要先创建数组然后还对数组进行初始化,如果这么做的话,编译器还会报错说你重定义,所以大家记住数组的初始化包括创建数组的过程,那么我们又如何对数组进行初始化呢?我们接着往下看。

如何对数组进行初始化?

1.完全初始化:
#include<stdio.h>
int main()
{
	int arr[5]={1,2,3,4,5,};
	return 0;
}

  这个就是我们的完全初始化的例子,右边的数据都是我们确定好以后要用的数据,这些数据有多少我们就在[ ]里面填入多大的数字,这些数据我们就用逗号将其分割开,根据上面的初始化来看我们是把1赋值首元素(arr[0]),把2赋值给了第二个元素(arr[1]),后面就依次类推,这就是我们的完全初始化,但是大家可以发现的一点就是,这种初始化的方法其实并不是很好,因为我们得知道所有的数据,才能对其进行完全初始化,但是有时候我们是不知道一些数据的,我们得根据下面的一些代码和数据来算出一些新的数据来,然后再存入这个数组当中,如果遇到这种情况的话,我们是不可能将其完全初始化的,所以为了解决上述的问题,我们就提出了一个新的初始化的方法叫做不完全初始化。

2.不完全初始化
#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3 };
	return 0;
}

  我们可以看到,虽然我们的数组的大小是5,但是我们右边进行初始化的数据却只有3个,我们没有因为数组的大小是5个元素,而将这5个元素都进行了初始化,这就是我们所谓的不完全初始化,那么这时有小伙伴就想啊,你只初始化了三个数据,那么最后的两个数据不就没有进行初始化了嘛?那么他的值不还是垃圾数据了吗?这就好比煮饭煮夹生了,不吃得饿肚子,吃了又不好吃的感觉,好那么这里我就来跟大家说一下,如果我们对数组采用不完全初始化的话,我们编译器会对没有初始化到的数据统一将其初始化为0,那么我们这里的arr[3],arr[4]都会被初始化为0,而不会是垃圾数据,我们可以对通过监视来看看:请添加图片描述
我们发现确实如我们想的一样,但是看到这里大家有没有一种感觉就是,我们上面的两种初始化方式,都有点美中不足的感觉,因为我们这里的数据不是很多,我们可以数的过来,但是如果我们哪天遇到了数据很多的情况那又该如何进行初始化呢?我们不可能全部一个一个的把他们数出来吧,如果数少了编译器就直接报错了,你还要再数一遍,如果说估计一个大概的话,又会造成内存的大量的浪费,所以为了解决上面的问题我们又有了一种新的初始化方法,叫做不指定长度的初始化。

3.不指定长度的初始化。

我们来看看不指定长度的初始化长什么样:

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5 };
	return 0;
}

  我们可以看到这里有个非常鲜明的特点就是我们的方括号[ ]里面是没有具体的值,如果你不给他值的话我们编译器就会直接根据你后面初始化的数据多少来确定这个数组的大小,比如说上面的例子我们在初始化的时候有5个数据,那么编译器就会自动的在括号里面添加一个5,我们可以用这段代码进行验证:

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5 };
	int a = sizeof(arr);
	printf("%d", a);
	return 0;
}

我们这个sizeof算的就是我们这个整个数组大小,我们运行一下就可以知道我们这个数组对应的大小是20,而且我们数组中的每个元素的类型是int ,所以我们就可以推断出如果我们在[ ]里面不添加数据的话,编译器确实是会根据后面初始化的数据的多少来自动的添加上去,这样就很好的解决了我们数据太多不好数的问题。那么看到这里想必大家应该知道如何对这个一维数组进行初始化了,那么接下来我们来看看如何对一个一维数组进行赋值。

3.一维数组的引用

  之前我们在讲操作符的时候就提到过这个[ ]操作符,叫做下标引用操作符,这个操作符就可以在数组定义之后使用这个数组里面的元素,其基本的引用形式为:数组表示符[ 下标 ] 这里就要提醒大家一点就是我们这里的下标他是从0开始的,也就是我们数组的第一个元素的下标并不是1而是0,相对应的我们数组的第二个元素的下标是1而不是2,那么后面就可以以此类推,假如一个数组当中有20个元素,那么他的下标的范围应该就是从0到19,而不是1到20这里希望大家注意一下,这里我们就来看一个简单的例子:比如说我们这里创建一个能装10个整型数据的数组并且将其初始化为:1 ,2 ,3, 4, 5 …10然后我们再创建两个变量a和b并且将这个数组的第6位赋值给a,将数组的第9位赋值给变量最后打印出a和b这两个变量的值这个例子很简单啊我就不对其进行讲解了,大家直接看代码吧:

#include<stdio.h>
int main()
{
	int arr[20] = { 1,2,3,4,5,6,7,8,9,10 };//创建数组并且初始化
	int a = 0;//创建变量并且初始化
	int b = 0;//创建变量并且初始化
	a = arr[5];//将数组第六位赋值给a
	b = arr[8];//将数组第九位赋值给a
	printf("%d", a); 
	printf("%d", b);
	return 0;
}

啊这里大家就只用注意下标的问题就可以了,那么这里我就来跟大家讲一下另一个知识点:因为我们刚刚学习道理如何讲数组里面的一个元素提取出来,而且我们还知道一点就是sizeof(arr)表示的是整个数组的大小,sizeof(arr[0])表示的是数组一个元素的大小,那么我们用sizeof(arr)/sizeof(arr[0])得到的结果是不是就是我们这整个数组的数据个数啊,那么这里跟大家讲一下,因为我们后面会遇到很多这种形式。

4.一维数组的赋值

  既然我们知道了如何引用数组中的元素,那么我们这里就来跟大家讲一下如何来跟一个一位数组进行赋值,比如说我们将一个能够装下10个数据的int类型的数组不完全初始化为0,然后我们要将这个数组中的元素按照顺序赋值成1,2,3…10,并且将数组中的每一个值都打印出来那么这时我们有同学就会说啊:这还不简单吗!这不是跟我们初始化的时候一样的吗?我们把前面的数据类型去掉不就够了吗!如果按照这位同学的想法来看我们数组的赋值应该长成这样:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for (i = 0; i < 10; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

其实大家编译一下就会发现压根就编译不过去,因为在c语言当中除了初始化以外是不允许使用花括号列表的形式进行赋值,那么这时候有小伙伴啊脑洞大开说啊我能不能再创建一个数组并且将其初始化为1,2,3,4…再把他赋值给我们这个数组,我们把他的想法用代码写下来就是这样:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	arr = arr1;
	for (i = 0; i < 10; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

我们编译一下就会发现其实是编译不过去的,因为在我们c语言当中是不能够将数组看成一个单元赋值给另一个数组的,那么我们的数组该如何来赋值呢?那么这里我们就只能用for循环来对数组中的元素个一个的来进行赋值。当让其他的循环也可以哈我们的代码如下:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = (i + 1);
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

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

  我们的一维数组虽然是数组但是,他依然还是会想内存申请空间,那么这个空间是如何分布的呢?我们一个数组这么多个元素难道是东存一个元素,西存一个元素,之间没有任何联系的吗?当然不是我们可以先创建一个数组然后使用取地址操作符(&)将他们每个元素的地址取出来看看中间是否什么规律,那么我们的代码如下:`

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i <sz ; i++)![请添加图片描述](https://img-blog.csdnimg.cn/4628febfe8944dfdacf6777a654b791f.png)

	{
		printf("arr[%d]的地址为:%p\n", i,&arr[i]);
	}
	return 0;
}

我们来看一下打印的结果是:请添加图片描述
我们仔细的看一下我们就可以发现随着数组下标的增长,元素的地址也曾规律性的增长并且这些地址之间相差都为4,我们这个数组里面元素的类型都为int类型,而且我们知道这个int类型所创建出来的变量的大小也是恰好为4个字节,那么这是不是就说明了一件事呢?就是我们数组在内存中的存储是连续的不会中断的,我们可以吧int类型改成char类型来看看,因为我们知道char类型所创建出来的变量大小是1个字节,那我们将代码稍作修改:

#include<stdio.h>
int main()
{
	char arr[10] = { '0'};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i <sz ; i++)
	{
		printf("arr[%d]的地址为:%p\n",i, &arr[i]);
	}
	return 0;
}

我们再来看看这段代码的运行结果为:
请添加图片描述
这下子是不是就感觉更加的清除了啊,那么这里我们就知道了数组在内存中的存储是连续的。

二.二维数组

1.为什么会有二维数组

  刚开始学编程的小伙伴们肯定会有些困惑,就是我们为什么会有二维数组呢?那么这里就用个例子来告诉大家为什么会有二维数组这个东西。 有一个气象研究院为完成他的研究项目要分析5年内每个月的降水量数据,她寿阳县要解决的问题就是如何表示数据。因为我们知道一年有12月,那么5年就是60个月,所以我们这里就有了第一个方案就是创建60个变量,每个变量都存储一个数据项,那么这个方案虽然可以但是不是很合适,因为这样的话我们第一在写代码的时候就很浪费时间,并且我们在统计数据的时候也不方便整理,所以我们这里就有了第二个方案就是,创建5个一维数组,一个一维数组里面装的就是一年的下雨数据,这个方案看起来就方便多了,但是依然有个弊端就是如果我们要统计50年的数据呢?那是不是又有点力不从心的感觉,那么这里就到我们二维数组出手的时候我们可以通过二维数组来轻松的解决我们上述的问题,那么接下来我们就来讲讲二维数组的使用方法。

2.二维数组的创建

  跟一维数组的相差不大,但是我们得换一个角度来理解二维数组,其实我们可以将二维数组看成一个一维数组,但是这个一维数组里面的每个元素又是一个个的数组,我们二维数组的基本形式就是这样:
(数据类型 数组名[常量表达式1] [常量表达式2])那么既然我们把这个二维数组看成是一个装的是数组的数组,那么这里的基本形式我们就可以这么理解:我们将两个方括号分开,第一个方括号里面的数字表示的就是这个外层数组里面装了几个数组,并且这些数组的名字就为arr[0],arr[1],arr[2]等等,而后面一个的方括号则就表示的是这个里面的数组里面有几个元素(就是数组里面的数组中有几个元素),我们可以举一个例子就是我们我们先创建了一个数组为int arr[3][4],那么这个int arr[3][4]表示的意思就是大数组arr里面装了三个小数组,这些数组的名字就是arr[0], arr[1], arr[2],而这三个小数组里面又分别装了4个数据类型为int的元素,我们可以画个图来理解

在这里插入图片描述
因为我们说二维数组是一个内部元素为数组的数组,那么我们就可以将数组里面的数组提取一个出来如上图所示,又因为int arr[3][4]有三个这样的数组,所以我们就可以将三个如上图所示的数组并列起来,就构成了一个数据表:
请添加图片描述
如果我们说要用到二维数组中的arr[1][2]的值,那么这个就表示的是 数组名为arr[1]数列中的第三个元素,那么也就对应的是这个表中的第二行第三列,所以我们还可以以另一种形式来理解这个二维数组就是将我们的二维数组看成是一个表,第一个方括号里面的值看作是行,第二个方括号里面的数据表示的是列即可。那么看到这里相信各位小伙伴应该已经理解我们的二维数组的意思,那么这样的话我们二维数组如何进行创建就是易如反掌了基本格式就是这样:(数据类型 数组名[常量表达式] [常量表达式])

3.二维数组的初始化

因为我们知道二维数组是一个元素为数组的数组,那么如果是这样的话,我们在进行初始化的时候就必定会遇到一些问题,比如:

1. 我们该把哪些数据分给第一个数组,我们又该把哪些数组分给第二数组等等?
2. 如果有些数组并没有完全初始化又会怎么样?
3. 我们能不能跟一维数组一样初始化的时候在方括号里面不加数据?
4. 我们能不能强行的将某些数据初始化给某一个数组?

第一种初始化

第一种初始化的方式与我们一位数组的初始化的方式一模一样我们直接将这个二维数组看成一个整体对其进行初始化例如下面的例子:

#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10};
	return 0;
}

如果我们采用这样的初始化的方法的话,我们这个二维数组就会按照顺序来依次将这些数据装到我们的内部数组当中,因为我们这里有3个小数组,并且每个小数组能装4个数据,所以我们这里就会将数据1,2,3,4装到数组名为arr[0]这个数组里面,将5,6,7,8装到数组名为arr[1]这个数组里面,将9,10装到数组名为arr[2]的这个数组里面,又因为我们这里的小数组能装4个元素而我们这里的arr[2却只初始化了两个元素所以剩下没有初始化的位置就会将其初始化为0,那么这个就是我们的第一种初始化的方法,形式与一维数组一样,分元素也是一个一个按照顺序分给里面的小数组,没有分到的就将其初始化为0。

第二种初始化的方法

看了上面的第一种初始化的方法,其实我们发现有个问题就是如果我们用第一种初始化方法就必须得按照顺序,那我们能不能不按照顺序来进行初始化呢?比如说我只想将前三个个初始化给第一个数组后三个初始化给第三个数组,那我们第二种初始化方法就可以解决这个问题,我们可以将这些初始化的数据中加入花括号,比如我们将前三个数据放到一个花括号里面,再把第三个和第四个数据再放到一个花括号里面,那么这里的意思就是将前三个数据放到第一个数组里面把第三个和第四个数据放到第二个数组里面我们可以看一个例子

#include<stdio.h>
int main()
{
	int arr[4][4] = { {1,2,3},{4,5},{6,7,8,9},10,11 };
	return 0;
}

我们来看一下这个例子,我们将1,2,3放到第一个花括号里面,那么这就表示的是将1,2,3放到第一个数组里面,然后我们将4,5放到第二个花括号里面那么这就表示的是我们将4和5放到第二个数组里面,我们将6,7,8,9放到第三个花括号里面那么这就表示我们将这四个数据初始化到第三个数组里面,然后后面就没有花括号了,那么后面就会按照顺序依次初始化到第四个数组里面跟我们第一种初始化的规则一模一样,我们可以通过调试看到这个数组里面的内容:
请添加图片描述
那么看到这里不知道大家发现了一个细节没有大家看到这张图片的最右边,那么发现有个类型叫int[4],这说明了什么呢就?号看到这里我来问大家一个问题就是我们都知道变量都是有类型的比如说我们创建了一个int类型的变量,那么这个变量的类型就是int型我们创建一个float类型的变量,那么这个变量的类型就是float型,那么大家想一下数组有他对应的类型吗?当然有比如我们初始化了一个数组int arr[4]那么他的类型就是int [4]把他的数组名去掉就可以了,那么我们再来看这个二维数组我们之前说过二维数组可以将其看成一个内部为一维数组的一维数组,并且第一个方括号加上里面的值加上二维数组名就是我们里面一维数组的数组名,那么这些数组的名字那么这些一维数组的类型是什么呢?是不是就是把名字去掉就可以了也就是int [4]我们通过调试也可以看到跟我们的猜想一模一样,那么这里是不是也进一步的验证了我们的将一个二维数组看成一个内部为一维数组的一维数组的这个想法是对的。那么看到这里有小伙伴们回想着能不能这样,既然4和5在第一个数组和第三个数组之间那么我们能不能将第二个花括号去掉,然后他也应该被规划到第二个数组里面去那么我们这里可以试一下请添加图片描述
我们可以看到这样的想法是不行的哈!那么看到这咯i想必大家应该知道如何第二种初始化的方法,那么这里我们就来讲讲第三种初始化的方法。

第三种初始化

我们上面说一维数组的初始化的时候我们说了这么一种情况就是我们的方括号里面可以不加值,编译器会自己根据后面初始化的内容来判断这个方括号里面应该填入多少,那么我们的二维数组可以这样吗?答案是当然可以但是我们只能省略第一个方括号里面的值不能省略第二个,那么这里大家想想为什么呢?其实答案也非常的简单哈,之所以必须得给第二个方括号里面的数据是因为我们这个数据表示的是我们的小数组里面有多少个元素,而我们初始化的元素是固定的啊,既然我们知道了一个数组里面会有多少个元素的话,那么我们是不是能够推出来有多少个数组啊,我们直接将这些数据往数组里面加,这样一定能够得出这些数据需要多少个数组,那么反过来就不一定了,你告诉我有多少个数组你是推不出来每个数组有多少个元素的,比如说我们初始化了10个数据,你告诉我有4个数组,那我可以每个数组可以有4个数据,也可以每个数组有5个数据等等等,因为在我们初始化的时候剩下没有初始化到的位置会自动将其初始化为0,所以告诉我们有多少个数组是没有办法推出这个数组里面有多少个元素的看到这里我们再来举一个例子想必大家能够理解的更好的理解:

#include<stdio.h>
int main()
{
	int arr[][4] = { 1,2,3,4,5,6,7,8,9 };
	return 0;
}

想必看到这个例子大家应该都知道这里到底是如何来分配数组的,我这里就不再多进行赘述我们直接看监视里面的结果是怎么样的
请添加图片描述
确实跟我们想的一样哈,其实我们第三种初始化方法可以与第二种初始化方法结合在一起我们直接来看一个例子:

#include<stdio.h>
int main()
{
	int arr[][4] = { {1,2},{3,4,5},{6,7,8,9} };
	return 0;
}

我们来看看监视的结果
请添加图片描述
这里大家应该都能明白为什么哈不多赘述。

4.二维数组的使用

我们说过二维数组其实是一个内部数据为一维数组的一维数组,那么我们使用这个二维数组也就变得很简单的哈我们首先可以根据这个第一个方括号找到是内部的第几个数组,然后再根据这个第二个方括号找到是内部数组中的第几个元素,这样子我们二维数组的使用就很简单了,比如说arr[3][4]他表达的就是内部的第4个数组中的第5个元素,但是这里还是要提醒大家的就是虽然是二维数组但是我们这里的下标依然是从0开始,不是从1哈。我们来看个例子哈我们先初始化一个二维数组为int arr[3][4]然后将这个数组里面元素赋值为1,2,3,4,5,6 …12.我们之前说一维数组给每个元素进行赋值的时候是用的一个for循环,那么我们的二维数组给每个元素赋值则需要用到两个for循环,因为每经历一次外部循环,我们的内部循环都会经历一次完整的循环,所以我们将这个两个for循环的嵌套用到二维数组里面就变成了先将大数组里面的第一个小数组里面的元素全部赋值之后再将大数组中的第二个小数组中的元素全部赋值,那么看到这里想必大家应该知道了如何对一个二维数组进行赋值那么我们的代码就如下:

#include<stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;
	int j = 0;
	int k = 1;
	for (i = 0; i < 3; i++)
	{
		for(j=0;j<4;j++)
		{
			arr[i][j] = k;
			k++;
			printf("arr[%d][%d]=%d\n", i, j,arr[i][j]);
		}
	}
	return 0;
}

5.二维数组在内存中的存储

我们上面讲过我们的一维数组在内存中的存储是连续的,并且是按照数组的下标而有序的呈规律的变化,那么我们的二维数组在内存中的存储也是这样的吗?如果确实是连续de,那么他的存储的顺序又是怎么样的呢?是先把第一个数组的第一个元素存完再存第二小组的第一个元素等等还是先把第一个小数组的全部元素存完再来存第二个数组呢?那么带着这些问题我们就来写段代码将这些数组的

#include<stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;
	int j = 0;
	int k = 1;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			arr[i][j] = k;
			k++;
			printf("arr[%d][%d]=%p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

我们来看看折断代码的运行结果
请添加图片描述
我们观察这些地址便可以发现我们的二维数组的存储确实是一段连续的空间,因为我们这里的二维数组是int的类型,所以我们还发现我们这里的存储的规律其实是先将第一个小数组的全部元素存储完再来存储第二小数组的全部元素这样依次往后。我们可以画一个图来理解,我们这里是一段连续的空间,那么我们的二维数组的存储就是这样的:
请添加图片描述
那么看到了这里想必大家应该知道了二维数组的存储问题,那么接下来我们就来看看字符数组又是怎么样的。

三.字符数组

1.字符数组的创建

其实字符数组的创建和一维数组是一样的,那么我们这里就不再过多的赘述,我们直接看一个例子:

#include<stdio.h>
int main()
{
	char arr1[10];
	char arr2[20];
	return 0;
}

跟我们的一维数组一样很好理解。

2.字符数组的引用

这个跟我们的一维数组也是一样的,我们的字符数组的下标也是从0开始,比如我们可以看下面的例子,一个字符数组里面装的hello我们要将这个数组里面的第三个字符打印出来,那么我们的代码如下:

#include<stdio.h>
int main()
{
	char arr1[5] = { 'h','e','l','l','o' };
	printf("第三个字符为arr1[2]=%c", arr1[2]);
	return 0;
}

跟一维数组一样这个就很好理解哈

3.字符数组的初始化

那么到了这里我们的字符数组的不同之处就出来了,那么接下来我们就来看看字符数组的初始化有什么不同?

第一张初始化方式

逐个字符赋值给数组中元素,这个很好理解定义一个包含5个元素的字符数组,在初始化的大括号中,每个字符赋值给一个数组元素,但是大家要注意的一点就是我们的字符得加个单引号并且字符之间得用逗号将其隔开,我们看个例子就明白了。

#include<stdio.h>
int main()
{
	char arr1[5] = { 'h','e','l','l','o' };
	char arr2[5] = { 'w','o','r','l','d' };
	return 0;
}

第二种初始化方法

如果初值个数与预定的数组长度相同,在定义时可以省略数组的长度(就是方括号里面的值),系统会自动根据初值的个数来确定数组的长度比如说:

#include<stdio.h>
int main()
{
	char arr1[] = { 'h','e','l','l','o' };
	char arr2[] = { 'w','o','r','l','d' };
	return 0;
}

第三种初始化方法

利用字符串给字符串给字符数组赋初值,因为我们的数组是可以存放字符串的,所以我们可以用字符串给字符数组初始化,但是大家要注意哈我们字符串的外面是双引号,不是单引号。我们来看个例子:

#include<stdio.h>
int main()
{
	char arr1[] = { "hello"};
	char arr2[] = "hello";l
	return 0;
}

其实我们外面的花括号加不加都无所谓哈。

4.不同初始化方法之间的不同

虽然我们有三种初始化的方法,但是这些初始化的方法还是有些不同,首先我们的第一种和第二种初始化方法所得到的效果是一样的,但是这两种效果和第三种的效果会有些不同,我们可以看一个例子:

#include<stdio.h>
int main()
{
	char arr1[] = { 'h','e','l','l','o' };
	char arr2[] = "hello";
	int a = sizeof(arr1);
	int b = sizeof(arr2);
	printf("arr1占的内存大小为:%d\n", a);
	printf("arr2占的内存大小为:%d\n", b);
	return 0;
}

我们可以看到外面将两个数组初始化为同样的值,但是外面采用了不同的初始化的方法,然后再用sizeof求得这这两个数组的大小,我们可以看一下这两种初始化之后的结果会不会有什么不同呢?我们来看一下运行的结果为:
请添加图片描述
我们发现虽然我们对这两个字符数组初始化了相同的内容,但是我们采取的初始化的方式不同所以造成的结果也不同这是为什么呢?我们可以再在下面求一下这两个字符数组的长度是不是一样的如果我们这个数组里面装了5个字符那么它的长度就应该是5,那么这里我们就可以采用strlen这个函数来求得我们这个字符的长度,我们来看看修改后的代码:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = { 'h','e','l','l','o' };
	char arr2[] = "hello";
	int a = sizeof(arr1);
	int b = sizeof(arr2);
	printf("arr1占的内存大小为:%d\n", a);
	printf("arr2占的内存大小为:%d\n", b);
	int len1 = (int)strlen(arr1);
	int len2 = (int)strlen(arr2);
	printf("数组arr1的长度为:%d\n", len1);
	printf("数组arr2的长度为:%d\n", len2);
	return 0;
}

我们来看看这段代码的运行结果:
请添加图片描述
我们发现这这个数组arr1的长度竟然是37这是为什么呢?这里我们先不慌我们看看将这两个数组打印出来看看情况是怎么样的我们的代码如下:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = { 'h','e','l','l','o' };
	char arr2[] = "hello";
	int a = sizeof(arr1);
	int b = sizeof(arr2);
	printf("arr1占的内存大小为:%d\n", a);
	printf("arr2占的内存大小为:%d\n", b);
	int len1 = (int)strlen(arr1);
	int len2 = (int)strlen(arr2);
	printf("数组arr1的长度为:%d\n", len1);
	printf("数组arr2的长度为:%d\n", len2);
	printf("arr1的内容为:%s\n", arr1);
	printf("arr2的内容为:%s\n", arr2);
	return 0;
}

我们来看看打印的结果如何:
请添加图片描述
啊这里为什么会那么这里就得讲讲我们这两个初始化的区别了,我们可以通过监视看一下这两个数组中到底存放了什么?请添加图片描述
我们发现我们在用字符串初始化数组的时候会自动多出来一个”\0“,那么这个"\0"的作用是什么呢?它的作用就是字符数组的结束标志,我们字符数组在求其长度或者以字符串的形式输出其内容的时候都是以看到\0才停止的,如果没有看到\0,就会继续往后面的内存进行读取数据直到看到\0才截至,这也是为什么会出现烫烫烫的原因,而我们用字符串的形式初始化我们数组的时候,会在字符串的末尾自动添加一个\0上去,这也是为什么我们的arr2的大小要大于arr1,长度却小于arr1的原因,所以我们在逐个字符赋值给数组元素的时候尽量自己添加一个\0上去,这样就可以防止这样的情况发生,但是不有小伙伴们可能就要问了,有时候我逐个字符初始化的时候也没有出现这样的情况啊,比如说这样:

#include<stdio.h>
int main()
{
	char arr[10] = { 'h','e','l','l','o' };
	printf("%s", arr);
	return 0;
}

这是因为我们这里给定这个字符数组的大小,而且我们初始化的内容却并没有将里面的内容完全初始化,还剩下来了一部分,所以这剩下来的一部分就会自动的初始化为0,而0对应的ascall值却又恰好为’\0’,所以我们在打印数组的时候就会自动在这里停止,但是你要是将这10个位置全部都初始化,并且初始化的内容不为\0的话还是会出现不会停的情况,比如说这样:

#include<stdio.h>
int main()
{
	char arr[10] = { 'h','e','l','l','o','w','o','r','l','d' };
	printf("%s", arr);
	return 0;
}

我们来看看这个的打印结果为:
请添加图片描述
对吧这就是我们两种初始化带来的不同,那么看到这里我们知道了字符数组并不要求最后一个字符为’\0’,甚至可以不包括’\0’,但是是否要加’\0’,得根据自己的需求来决定。由于系统对于字符串常量会自动加上一个’\0’,因此为了使处理的方法统一,且便于测定字符串的实际长度,我们在字符数组中也常常人为的加上一个‘\0’。

5.字符数组的输入和输出

字符数组的输入和输出可以采用两种格式字符:%c和%s,其中格式字符%c 可以实现字符的逐个输入和输出,例如下面的代码:

#include<stdio.h>
int main()
{
	char arr[10] = { 'h','e','l','l','o' };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%c\n", arr[i]);
	}
	return 0;
}

我们看一下我们的打印结果:
请添加图片描述
确实是将数组中的每个元素一个一个的输出那么我们再来看看另一种输出的方式:%s,这种输出方式就是将整个字符数组输入或输出我们来看个例子:

#include<stdio.h>
int main()
{
	char arr2[] = "hello";
	printf("%s", arr2);
	return 0;
}

我们来看一下输出的结果为:
请添加图片描述
确实是将整个数组输出出来哈,那么看到这里我们的字符数组就结束了,想必大家应该收获很多吧。

四.数组的越界现象

我们都知道数组的下标是由范围限制的,数组的下标=规定是从0开始的,如果数组有n个元素,那么最后一个元素的下标就是n-1,所以我们的小标如果小于0,或者大于n-1,就算数组越界访问了,超出了数组的合法空间访问,因为我们c语言本身是不做数组下边的越界检查的,所以编译器就不一定会报错,但是编译器不报错不意味着程序就是正确的,所以程序员写代码的时候最好自己做越界的检查。那么我们这里来谈谈为什么编译器不会做越界的检查这是因为c语言的相信程序员的原则,不检查边检,c程序可以运行的更快。编译器没必要捕获所有的下标,因为在程序运行之前,数组的下标值都没有确定,因此为安全起见编译器必须在运行时添加额外的代码检查数组的每个下标值,这会降低程序的运行速度,所以我们的c语言就相信我们的程序员能够写出正确的代码,这样我们的程序就会运行的更快,但是不是所有的程序员都能够做到这点所以就有了数组的越界现象。

五.数组名是什么

在一维数组当中数组名在一般情况下表示就是我们数组的首元素的地址,但是有两个情况除外:

1. sizeof(数组名)这里的数组名表示整个数组,所以我们这里的sizeof计算的就是我们整个元素的大小。
2. &(数组名)这里的数组名表示就是整个数组,所以我们这里取的就是我们整个数组的地址。

除了这两种情况外其他的都表示首元素的地址,但是看到这里有些小伙伴们可能就会感觉到一些疑惑了,整个数组的地址,这是什么意思啊?于是它试着将这个地址打印出来可是发现这个地址跟我们的第一个元素变的地址是一模一样的,我们可以看一下代码:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr);
	return 0;
}

我们可以看一下运行的结果:
请添加图片描述
确实是一样的,那么这有什么区别呢?我们可以试着将这样地址加1再看看地址会发生什么样的变化:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr[0]+1);
	printf("%p\n", &arr+1);
	return 0;
}

我们看看运行的结果为:
请添加图片描述
我们发现加一之后我们的结果却变得不一样了起来,我们可以试着将这些值向减就会发现我们的&arr+1他跳过的就是一整个数组,而我们的arr[0]+1则跳过的是数组中的一个元素,这就是我们数组地址和数组元素地址的区别,那么我们的二维数组的数组名又表示什么呢?,当然跟我们一维数组是一样的表示的也是首元素的地址,但是这个首元素并不是指的第一个小数组的第一个元素的地址,而是值得第一个小数组的地址,大家可要注意一下了,那么我们数组名方面的内容就这么多大家注意一下就可可以了。

六.指针和数组

我们说过数组名表示的是数组中的首元素的地址,而我们数组名后面的[ ]里面的元素则可以用来表示的数组中的第几个元素,那要是按照这么说的话我们的一维数组&arr[1]是不是就可以表示成&arr+1,因为我们数组里面装的是同样类型的数据,所以我们加1之后就会跳过该数据类型所对应的大小内存,然后指向新的地址,而这个新地址就是该数组中下一个元素的地址,所以&arr[0]就可以等价为arr+0,arr[2]就可以等价为*(arr+2)等等,但是这里还是有个问题需要大家注意一下的就是 *arr+2与*(arr+2)是不同的,第一个是解引用之后再加上2,也就是先得到了第一个元素的值然后再对这个值加上2而第二个是地址先加2(就是指向第三个元素)再对其解引用,希望大家注意一下这之间的区别,那么最好再来提一下我们再给函数传数组的时候要注意的一件事就是我们数组的传参有两种形式:
第一种:
就是直接传数组名,并且用数组进行接收,但是这里的数组接收必须得在数组后面加上[],之所以会出现这样的形式是为了我们传参的形式的统一,你看传数组用数组接收多么统一对吧。我们来看个例子

#include<stdio.h>
void bubble_sort(int arr1[])
{
	int i = 0;
	for (i = 0;i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	bubble_sort(arr);
	return 0;
}

第二种:
因为我们知道数组名实际上是首元素的地址所以我们这里可以把数组名传过去再用(元素的数据类型)*来接收,我们来看个例子 :

#include<stdio.h>
void bubble_sort(int* arr1)
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	bubble_sort(arr);
	return 0;
}

这两种方法都可以哈。

七.多维数组

学习了一维数组和二维数组,想必大家肯定在好奇有没有三维数组啊?答案是肯定有啊,关于如何来理解多维数组我们可以将这方面的思想与二维数组结合起来,我们说二维数组就是一个内部元素为一维数组的一维数组,那么我们的三维数组就是一个数组里面装的是数组,而装在里面的数组的里面的元素又是数组,啊就跟俄罗斯套娃一样哈,至于更多维的数组我们可以按照这种思想依次推理,我们把一维数组看成一列数据,那么二维数组就是一面表的数据,而三位数组就可以理解为一本书的数据,那么我们的四维数组就可以理解为一书架的数据。那么看到这里想必大家应该能够理解多维数组了,那么我们数组的内容也就结束了。

八.小结

看到这里我们关于我们数组的内容就讲完了,那么看到这里想必大家能够对数组有了一定的了解,那么接下来我们就会用到数组来写几个小游戏,我们不见不散。

点击此处获得本章代码

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶超凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值