zl程序教程

您现在的位置是:首页 >  其他

当前栏目

一维数组与二维数组———详细解读及一些注意事项

2023-03-07 09:43:18 时间

目录

一维数组

一维数组的创建及初始化

所谓数组,就是同一种元素的集合。一维数组的表达式为:

数组元素类型 +数组名+ [ 常量表达式];

#include<stdio.h>
int main()
{
	//元素类型为 int ,名为arr的共10个元素的数组
	int arr[10];

	//元素类型为 double ,名为arr1的共10个元素的数组
	double arr1[10];

	//元素类型为 char ,名为arr2的共10个元素的数组
	char arr2[10];

	//元素类型为 float ,名为arr3的共10个元素的数组
	float arr3[10];

	return 0;
}

这里注意一点,在C99标准里有变长数组的概念,所谓变长数组并不是指数组会变长,而是指用整型变量或表达式声明或定义的数组。如下所示:

int n=10;
int arr[n];

这里要注意的是,变长数组不能初始化,而且在一些编译器里使用变长数组,编译器会报错,比如在vs里就不能使用。

在创建数组的同时,给数组的内容一些合理值,这就是数组初始化。如下:

int main()
{

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	//当不指定数组大小时,数组大小根据后面内容来确定
	int arr1[] = { 1,2,3,4,5 };

	//不完全初始化,只初始化数组前三个元素,其余为0;
	int arr2[10] = { 1,2,3 };

	//随机初始化
	int arr3[10];
	return 0;
}

这里我们还要知道,数组是使用下标来访问的,下标是从0开始。

一维数组在内存中的存储

看如下代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	//sizeof用来求数组大小,通常用如下表达式来求数组大小
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (i = 0; i < sz; ++i)
	{
		//%p打印地址专用
		printf("&arr[%d] = %p\n", i, &arr[i]);
	}
	return 0;
}

这里我们可以看到,每一个数组后面的地址都会比前面大4,而我们知道,在32位环境下,一个地址就是32个二进制位,存储起来也就是32个比特位空间,即4个字节,也就是说,这些数组元素的地址随着下标的增长,是由低地址向高地址连续存放的!

二维数组

二维数组的创建及初始化

看下图:

#include<stdio.h>
int main()
{
	//3行4列,int类型,名为arr的二维数组
	int arr[3][4];

	//3行4列,char类型,名为arr1的二维数组
	char arr1[3][4];

	//3行4列,double类型,名为arr2的二维数组
	double arr2[3][4];

	return 0;
}

二维数组与一维数组相比较,多了行与列的区别。同样,我们也可以在创建数组的同时给数组内容赋合理的值,即二维数组初始化,大家看下图:

从这里我们可以看出,在二维数组初始化时,可以省略行,但不能省略列,同时,初始化的内容我们可以用{ }来限定。

同样,二维数组和一维数组一样,也是有下标的,从0行0列开始,如下:

二维数组在内存中的存储

与一维数组一样,我们看如下代码:

#include <stdio.h>
int main()
{
	int arr[3][4];
	//i控制行
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		//j控制列
		int j = 0;
		for (j = 0; j < 4; j++)
		{

			//打印arr里每个元素的地址
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

我们可以看到,在32位机器里,二维数组与一维数组一样,每个数组元素的地址都要比前面那个元素多4字节,由此,我们可以看出,二维数组在内存中的存储依然是由低地址向高地址连续存放,如图所示:

数组越界

我们知道,数组都是通过下标来使用的,而下标又是有范围的,数组的下标从0开始,假如一共有n个元素,则下标最大为n-1。 而数组的下标一旦超过这个最大值,就会造成越界访问,所谓越界访问,就是超出合法空间访问,也就是超过了数组所在空间,就好像这样:

通过一段代码来验证:我们来看一下下面代码运行的结果:

#include<stdio.h>

int main()
{
	int arr[4] = { 1,2,3,4 };

	
	printf("%d", arr[4]);
	return 0;
}

我们可以看到,编译器并没有报错,但我们打印出来的值却不在我们可控范围内,这就是数组越界。

同样,二维数组也存在数组越界:

数组注意事项

1

我们要知道,数组名在作为参数时,传送的是数组首元素的地址,并不是整个数组:

#include<stdio.h>
int print(int arr[])
{
	return sizeof(arr) / sizeof(arr[0]);
}

int main()
{
	int arr[3] = { 1,2,3 };
	int tmp = print(arr);
	printf("%d", tmp);

	return 0;
}

有的人可能会说,打印的结果是3,但其实并非如此: 因为在这里,print(arr),这里的arr实际上是首元素,即arr[0]的地址,传送过去后,sizeof(arr)其实求的就是首元素的大小,即这里的sizeof(arr)就等价于sizeof(arr[0]),所以最终结果为1.

数组名表示首元素地址,但是,有两种情况是例外的,就是size of(数组名),这里的数组名是表示整个数组。而我们上面的例子里,数组名arr先是作为参数传送的,所以传送过去的就是数组首元素的地址,所以求sizeof(arr)的时候,实际上求得是数组首元素的大小,注意分得清。

另外一种情况就是&数组名,这里表示的是取出整个数组的地址。这里的数组名表示整个数组。

除此之外,其余任何地方的数组名,都是表示数组首元素地址。

2

我们要注意数组中字符串类型的数组,具体看如下例题:

#include <stdio.h>
int main()
{
    char str[] = "hello boy";
    printf("%d %d\n", sizeof(str), strlen(str));
	return 0;
}

注意!:我们要知道,字符串结束的标志是\0,在这个数组里,实际上是有个\0的存在的,只不过被隐藏了,大家看下图所示:

而sizeof求的是数组所占内存空间的大小,char类型在 32位机器下,一个字符占用1个字节,这里加上\0一共是10个字符,所以一共是10*1=10个字节。 而strlen是专门用来求字符串长度的,\0不算在内,所以结果应该是9

同样,理清楚后,下面这道题也就很简单了:

acX实际上加上\0,一共8个字符,acY一共7个字符,并不等价 同样,在计算长度时,结束标志是\0,由于acY没有\0,所以长度随机值。

所以,正确答案为C。

二维数组主要应用:

三子棋与扫雷就运用到了二维数组,大家闲来无事可以写一下:

三子棋

扫雷

以上暂且讲到这,诸君加油。