值的集合——数组

一,数组的介绍

数组是一组类型相同的值的集合,在数学里,N表示自然数集,Q表示有理数集,R表示实数集,数组可以类比一下数学中的这些集合。数组里的数据,在计算机里是连续存储的。
数组有很多类型,包括整型数组,字符数组,指针数组,函数指针数组等等。
数组里每一个数称为数组的元素。

比如下面这些数组

int main()
{
	int a[5] = {0};//这是一个整型数组,数组里有5个数据,
	               //每个数都是int类型,a表示数组的名称,[]里的数表示数组的元素个数

	char b[] = { "abcdef" };
	//这是一个字符数组,数组的每个元素都是字符
	//[]里可以不给数字,如果不给,那么数组的大小就是,后面花括号内的元素个数,但是这里是7
	//因为字符串后面隐藏了一个\0

	int length = sizeof(b) / sizeof(char);
	printf("%d", length);//可以通过这种方式打印证明

	int a1[] = { 0 };
	int a2[] = { 0 };
	int a3[] = { 0 };
	int* c[] = {a1,a2,a3};//这是一个指针数组,每个元素都是指针类型

	return 0;
}

计算数组的大小,可以用到关键字sizeof,方式为sizeof(数组名),即可计算数组的大小,那么该怎么计算数组元素的个数呢,总不能一个一个数吧,有了上面的基础很容易想到,用数组的大小除以元素类型即可
也就是sizeof(数组名)/sizeof(类型名),这样算出来的就是元素个数。

二,数组的创建与初始化

创建数组时,需要按照

类型 数组名[ ];

这样的格式创建,类型是数组内元素的类型,不是数组的类型,[ ]里面的数字表示数组元素的个数,且这个数字必须是常量。如果没有数字,比如

int a[ ]={0};这种是对的方式,下面这种方式是错误的,因为b是一个变量
int b=5;
int c[b]={0};

那么数组里就一个元素0,元素类型是int,那么数组的类型是什么
呢?首先将数组名去掉,剩下的就是数组的类型,比如

int [1];这个就是上面数组的类型

在这里插入图片描述

通过vs调试可以看到,数组a的类型为,int[5],b的类型为char[7],7也表示数组b有七个元素,c的类型为int*[3]。

创建数组后,还要进行初始化。

int a[10]={0};
创建的同时就可以初始化,这样叫不完全初始化,只初始化了一个元素的值
int b[5]={1,2,3,4,5};
这样是完全的初始化,每个元素之间用逗号隔开

有了数据,接下来就介绍如何访问。

三,数组的访问

1,下标访问

数组可以通过下标访问,下标就是数据的一个标识符,通过这个标识符找到数据。下标从0开始,依次增加,最大值是数组元素个数减一。比如

int a[5]={1,2,3,4,5};
数据1的下标是0,数据2的下标是1…数据5的下标是4
元素访问方式为:数组名[元素下标];
如要访问数据3,则可以写为a[2];

int main()
{
	int a[] = {1,2,3,4,5};
	for (int i = 0; i < 5; ++i)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

通过一个for循环就可以打印数组内的元素,元素间用空格隔开。下标的最大值是元素个数减一,当下标大于等于元素个数时,还去访问的话,就会越界访问了。

2,指针访问

数组也可以通过指针访问,那么指针又是什么,通常讲的指针是指针变量,这个变量存储的是地址。在计算机中,每一个内存单元是一个字节,而每个字节又有一个地址,指针变量存储的就是这些地址,通俗的说,内存单元就像房间,房间里的人就是存的数据,这个房间号就是地址,我们可以通过这个房间号找到这个人,在计算机里,可以通过这个地址找到数据。

先看一个简单的代码

int main()
{
	int a[] = {1,2,3,4,5 };
	int* p = a;         //定义一个指针变量,*代表p是一个指针变量,
	                    //int代表p这个指针变量 指向的内存里 存储的数据的类型是int类型

	int sz = sizeof(a) / sizeof(int); // 计算数组的大小
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));      
	}
	return 0;
}

a是数组名,也是数组首元素的地址,正因为它是地址,所以才可以用指针变量存储。
p是一个指针变量,p+i代表这个指针跳过了i个整型。(在这里可以这么认为,但是不准确,后期会专门有指针的介绍和解析的。)
*(p+i)中的 *代表解引用,表示通过这个指针找到指针指向的这个数据。在依次循环,打印数据。

一个简图帮助大家理解。
在这里插入图片描述
如果a代表数组首元素的地址,那么打印是否可以这样呢

int main()
{
	int a[] = {1,2,3,4,5 };
	int sz = sizeof(a) / sizeof(int); 
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(a + i));
	}
	return 0;
}

如果可以这样打印,就能少定义一个指针变量了,也简化了代码
通过运行测试,发现,打印结果是正确的。

所以,*(p+i)==*(a+i)==a[i],这样就可以很好的解释为什么,下标最大值不能是数组的个数,因为如果下标是数组的个数,那么a+i,就跳过了i个整型,即把整个数组都跳过了,那么访问的就不是数组的数据。
关于数组的访问,指针访问的方法比较难理解,但更底层。接下来介绍二维数组

四,二维数组

形如

int a[][5]={0};的数组称为二维数组。
在类型名,初始化,数组名等方面,和一维数组无太大的差异
a后面的两个[]里的数字,前一个表示数组有几行,后一个表示数组有几列,第一个数字可以省略,第二个不能省略。

我们可以把二维数组看成一个一维数组,一维数组的每一个元素又是一个数组。
数组在内存里是连续存储的。
在这里插入图片描述
通过结果,我们可以看到,每个数据的起始地址相差4。%p是用来打印地址的占位符,&符号表示取地址,&a[i]表示,取出下标为i的元素的地址,通过循环就可以依次打印每一个元素的地址。

二维数组可以看成一维数组,那么存储时也是连续的吗
我们写个代码测试一下 。

int main()
{
	int a[][5] = {1,2,3,4,5,     //创建一个三行五列的数组
		          6,7,8,9,10,
		          11,12,13,14,15};  
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%p\n", &a[i][j]);
		}
	}
	return 0;
}

运行结果如图
在这里插入图片描述
可以看到,地址值之间也是相差4,所以二维数组的数据在内存里也是连续存储的。既然是连续存储的,在创建二维数组时,如果不给列数,那么数组就可能有很多个,不唯一,但是如果不给行数,比如上面的代码,只给了一个5列,可以通过元素个数除以列数算出行数,是唯一的。

int main()
{
	int a[5][] = { 1,2,3,          //五行数据可以这样放
		           4,5,6,
		           7,8,9,
		           10,11,12,
		           13,14,15 }; 

	int b[5][] = { 1,2,3,4,        //五行数据也可以这样放
				   5,6,
				   7,8,9,
				   10,11,12,
				   13,14,15 };
}

所以行可以省略,列不可以省略。

二维数组又该如何访问呢,其实上面已经展示了,通过a[i][j]的形式,i代表行,j代表列,i和j都是从0开始。也不能大于或等于[]里的数字。

int main()
{
	int a[][5] = {1,2,3,4,5,     //创建一个三行五列的数组
		          6,7,8,9,10,
		          11,12,13,14,15};  
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
	return 0;
}

通过下标访问,a[i][j]表示,第i行,第j列的元素,比如a[2][2]就是13。

数组名表示首元素的地址,而二维数组的首元素是一个一维数组,那么创建一个指针变量来接收这个地址,然后在加1,这个操作的结果是什么呢?

我们通过代码来测试一下

int main()
{
	int a[][5] = {1,2,3,4,5,     
		          6,7,8,9,10,
		          11,12,13,14,15};  
	int(*p)[5] = a;
	printf("%p\n", *p);
	printf("%p",*(p+1));
	return 0;
}

首先,解释一下 int(*p)[5]=a;这句代码,由于二维数组的首元素是一个一维数组,那么需要一个指针变量来指向这个一维数组,指向数组的指针叫做数组指针,这和前面的指针数组不一样,这两个概念很容易混,大家要注意一下噢。

*还是说明p是一个指针,和前面不同,加括号的原因是要让*和p先结合,括号的优先级高,这样p就是一个指针,去掉(*p)剩下的int [5]就是指针指向的数据的类型,可以看到,指向的数据是一个数组,里面有五个元素,每个元素是int型。

如果不加括号就变成int* p[5],这个大家应该很眼熟吧!这不就是指针数组嘛,p是数组名,数组的每个元素的类型是int*,所以这个括号是有必要加的。

了解到这,那么看一下运行结果吧
在这里插入图片描述

地址是十六进制表示的,前面都一样,只看最后两位,6C和58都是16进制,转化为十进制后分别是,108和88,它们相差20,刚好是5个整形,即20个字节。(关于进制的转换,后面也会详细的介绍)

通过结果,我们知道了,指向一个数组的指针加1操作,是跳过这个数组。
那么通过指针,该怎么访问二维数组呢
在这里插入图片描述
代码如下,运行结果如上。

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

*(*(p + i) + j)这句代码可能是难点,给大家一层层的分析,首先(p+i)是指跳过i行元素,存储的值是地址,前面在加一个*表示,对这一行的地址进行解引用,即找到这一行的数据。*(p+i)+j后的+j表示跳过这一行的j个数据,加一个*在进行解引用得到最后的表达式*(*(p + i) + j),即访问到第i行,第j列的元素,然后打印。

内容还是比较多的,关于指针的部分比较难,大家可以自己写一下代码,加深理解,有关数组的知识,就介绍到这里啦!

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值