栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

初学C语言:对于数组的一些见解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

初学C语言:对于数组的一些见解

前言:本人为C语言初学者,学识尚浅,研究程度存在很大的局限性,眼界很窄。以下所有观点仅代表个人见解和思路,各位游刃有余的前辈可以给予批评和指正!各位与鄙人同路的学子可相互探讨、发表看法,交换观点!

数组释意(个人理解):一组相同数据类型的数在内存中的连续储存

数组分为:一维数组、二维数组、多维数组

可能在我们心里,他们分别是这样呈现的:

 可是在内存中,他们如何排列呢?故我们做一个小测试,见如下代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int arr1[6] = { 1, 2, 3, 4, 5, 6 }; //一维数组
	int arr2[3][6] = { {1,2,3,4,5,6}, //二维数组
					  {1,2,3,4,5,6},
					  {1,2,3,4,5,6} };
	int arr3[2][2][2] = { {{1,2},{3,4}}, {{5,6},{7,8}} }; //三维数组

	for (int i = 0; i < 6; i++) //一维数组的地址打印
	{
		printf("&arr1[%d] = %pn", i, &arr1[i]);
	}

	putchar('n');

	for (int i = 0; i < 3; i++) //二维数组的地址打印
	{
		for (int j = 0; j < 6; j++)
		{
			printf("&arr2[%d][%d] = %pn", i, j, &arr2[i][j]);
		}
	}

	putchar('n');

	for (int i = 0; i < 2; i++) //三维数组的地址打印
	{
		for (int j = 0; j < 2; j++)
		{
			for (int k = 0; k < 2; k++)
			{
				printf("&arr3[%d][%d][%d] = %pn", i, j, k, &arr3[i][j][k]);
			}
		}
	}
	

	return 0;
}

(我总觉得三维数组的初始化写的有点问题)

我们得到了如下结果:

 int类型在本机所占空间为4byte,所以可以看到所有的数组在内存中都是连续排列的,不论维度!

而由以上代码,倒是引发了一些思考:

在对数组初始化时,我装了 {1,2,3,4,5,6} 总共6个元素,所以我在 [ ] 中填写的是6,表明有6个元素,但如果我初始化时不填满6个会怎样呢?

#include 

int main()
{
    int arr[6] = {1,2,3};
    
    return 0;
}

我们将数组打印,结果如下:

未被初始化的值自动填充为0,我们称这个为不完全初始化 ,于此,如果我们把6个都填满了,要多少给了多少,就称之为完全初始化。

但是注意,如果我们仅仅定义一个数组,但不赋值,这时候存放的可不是0!

例如:

#include 

int main()
{
	int arr[6];

	return 0;
}

如果我们打印里面的值,会是这样的:

因为我们只分配了空间却没有赋值,导致里面会是随机值。 

假如我们想偷懒,不想写这个6,也是可以的,但是这时,所有的初始化都是完全初始化,写几个值系统就会为你分配多少空间,就不存在填充0的情况了。

但是对于二维数组、多维数组来说,怎么省略呢?实际上,对二维数组分析,我们发现列数是可以省略的,也就是说,我知道了列就可以算出要多少行,但是这样说并不严谨,我尝试以更好的方式呈现。例如,我们这样声明一个数组:int arr[ ][4] = {1,2,3,4,5,6,7,8,9,10}; 这样系统会认为它是3行,分别是:1 2 3 4 、 5 6 7 8 、 9 10 0 0; 未赋值的会自动填充0!但如果我们写成:               int arr[3][ ] = {1,2,3,4,5,6,7,8,9,10}; 系统该怎么分配列呢?它可以是4列,5列,6列,7列……故我们只能省略行数而不能省略列数!

以此推断,所有数组的第一个方括号 [] 里的值是可以省略的。

即如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int arr1[] = { 1, 2, 3, 4, 5, 6 }; //一维数组
	int arr2[][6] = { {1,2,3,4,5,6}, //二维数组
					  {1,2,3,4,5,6},
					  {1,2,3,4,5,6} };
	int arr3[][2][2] = { {{1,2},{3,4}}, {{5,6},{7,8}} }; //三维数组

	return 0;
}

我们试着打印一下他们的值:

一样可以打印正确的值 

细心的小伙伴可能发现,数组在赋值的时候指定个数是6,但是在下标引用的时候却是由0到5,

这是由于数组下标是从0开始的,这一点一定要注意。

到这里,不得不说一下字符数组的特殊性。

思考一下,这两行代码一样吗?

char str1[] = {'A', 'B', 'C'};
char str2[] = "ABC";

打印一下不就知道了:

第一行代码好像有点烫?

在第一行代码中,我们明确的知道,计算机给它分配了3个字符的空间,分别是ABC,而对于第二行代码,我们赋值给它的是字符串,而字符串的末尾自带 '' 来标志字符串的结束,不信的话,我们可以F10调试一下:

所以,如果要进行单字符的逐个赋值,千万不要忘记了 '' 

这里不得不谈一下数组的一些特点:

或许大家知道,数组名直接使用的话,是等同于该数组第一个元素的地址的,所以数组名本质上是地址,即:arr == &arr[0],这两个是等价的。

但是有两个例外:1.sizeof(数组名); - 数组名表示的整个数组,如果数组名仅表示首元素的地址的话,那算出来的就是地址的大小了,64位机器是8,32位机器是4。所以sizeof运算符中的数组名是作为整个数组来计算的,计算出来的是整个数组的大小!所以进行函数调用时,传进去的是地址而已,在被调函数内部是不能使用sizeof计算整个数组大小的!

int arr[10] = {0};

int i = sizeof(arr);
printf("sizeof arr = %dn", i);

int j = sizeof(&arr[0]);
printf("sizeof &arr[0] = %dn", j);

2.&数组名,如果这里的数组名是首元素地址,那么对地址取地址是什么鬼!所以这里的含义是取出整个数组的地址,如果你想验证一下,写了这样的代码:

#include 

int main()
{
	char arr[4] = { 0 };

	printf("&arr[0] = %pn", &arr[0]);
	printf("&arr = %pn", &arr);

	return 0;
}

运行后发现:

介...介不是一样的吗?

其实不然,之前我们是用下标的方式来打印数组的,如果我们用 *地址 的方式来打印的话...

应该是这样的:printf("arr[0] = %dn", *arr); 但是我们怎么打印arr[1]呢?那当然是让地址加到下一个元素的位置不就好了,因为数组在内存中是连续排列的嘛,于是有些小伙伴直接想到了 arr++      这一思路。乍一想是不是有些不对?如果放在char类型的数组中,加1就是一个字节,没有问题,如果是放在int类型的数组中呢?加1不就乱套了吗?我们验证一下:

#include 

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

	int i = 0;

	for (i = 0; i < 6; i++)
	{
		printf("arr[%d] = %dn", i, *arr);
		arr++;
	}
	return 0;
}

这里编译器报错了,提示:

 大伙注意了嗷!地址在内存中是约定俗成的,也就是说1就是1,2就是2,这是定值,是不能修改的,就好比我给你xyz坐标,这个位置放的东西不知道是什么,也可能随时在改变,但这个位置永远不变!所以这里我们需要创建一个指针变量来储存地址,让它变为一个可以改变的左值!所以我们把代码改成下面这样:

#include 

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

	int i = 0;
	int* pa = arr;

	for (i = 0; i < 6; i++)
	{
		printf("arr[%d] = %dn", i, *pa);
		pa++;
	}
	return 0;
}

这样就可以正常打印啦!

但是这里加1居然加了4个字节???这是因为指针变量是int* 类型的,加1自动认为是到下一个元素的地址,所以是加4byte,当然,如果你的指针变量类型是char* ,那么加1也就是加1byte了 。

回归正题,我们知道如上概念后,那么数组的地址和数组首元素的地址到底有什么不一样呢?我们分别把它们加1,看看之后的地址是什么:

#include 

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

	printf("&arr = %pn", &arr);
	printf("&arr+1 = %pn", &arr+1);

	printf("arr = %pn", arr);
	printf("arr+1 = %pn", arr+1);

	return 0;
}

 

可以看到,数组的地址加1是加了整个数组的长度,而数组首元素地址加1只加了单个元素的类型的长度,即可见分别。

(这里的arr+1加了4个byte,是因为arr的类型是int* ,仔细想想是不是这么个道理)

(当然,这里你不能声明指针变量来储存&arr的地址,因为如果你声明int* p =&arr; 那么加1的时候会检索int* 类型的指针变量,所以自动加上4byte) 

对于多维数组来说,以二维数组为例,例如:int arr[2][3] = {0}; 如果我们仅&arr[0]和&arr[1],可以发现他们的地址相差12,所以是行优先的,以此类推,可以知道这里的列可以被省略,但是行一定要有,也就是说,对于地址而言,是层级关系,就像大括号里面包着中括号,中括号里面包着小括号,我们可以知道小括号的位置,但必须是在大括号的基础上,比如我们要知道arr[1][2]的地址,我们必须先有arr[1]才有arr[1][2],道理是一样的。换言之,我可以知道你在中国,然后知道你在湖北,然后知道你在武汉。但是如果我知道你在武汉,那么我一定知道你在湖北,一定知道你在中国。

如果我们用&arr[0]+1,加的还是4byte,道理同上括号内内容。

END

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1038673.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号