天天看点

手绘知识点——数组指针

各位老铁国庆节过的可还好,前天看了大阅兵,很激动,国富民强,我等也应该感到自豪~

来到指针系列第四篇,我们聊聊数组指针,即指向数组的指针。那您可能会问了,这东西有啥好说的,前边不都提了嘛,最多就是指针的加加减减,取个元素值啥的,其他也没什么可说的啊。哎(二声),事实胜于雄辩,咱们还是走一波~

首先大家应该回顾一下数组是个啥东西,按照官方的说法数组是一系列具有相同类型的数据的集合,划重点,一系列----相同类型,我们需要注意的是数组元素在内存中是连续存放的,区别于咱们前边提到的那些连续定义的变量,他们中间还有一些辅助的其他变量,而数组各个数组元素之间是紧密相连的,另外就是同一数组内所有元素的类型都是一样的,这样便于管理和各种操作。

为了精简文本就直接上代码吧,前两篇的博文文字太多,我自己看着都眼晕,这次我们尽量长话短话且说的直白接地气:

#include <stdio.h>

int main(){
	int arr[] = { 1, 10, 23, 101, 298, 376 };
	int *a = arr;
	int len = sizeof(arr) / sizeof(arr[0]);	
	for (int i = 0; i<len; i++) {
		printf("%d  ", arr[i]);
		printf("%d  ", a[i]);
		printf("%d  ", *(arr + i));
		printf("%d  ", *(a + i));
		printf("%#x\n", arr + i);
	}
	printf("%#x,%#x,%#x,%#x\n", arr, a, &arr[0], &arr);
	printf("%#x,%#x\n", arr + 1, &arr + 1);
	printf("\n");
	return 0;
}
           

我们定义了一个整型数组arr,并初始化了六个值,然后定义了数组指针a,它和arr一样都指向数组的第一个元素,即“1”。(这里大家注意数组名arr严格来说并不是指针,它是一个常量,其值为数组首元素的地址,所以数组名是不能进行++和--的,这里我我们为了方便暂且认为数组名就是一个指针,如果大家感兴趣可以去论坛搜一下这块,讨论的还是很激烈的);

接着往后的sizeof(arr)得到的是数组arr占据的内存大小,即整个数组所有元素共占有多少字节,而sizeof(arr[0])得到的是一个数组元素占据的内存大小,二者的商即数组的元素个数(这里我尝试用了strlen(),结果为1,这一点后续谈论字符串时再细聊);

再往后就是一系列的输出了,我们先看一下结果:

手绘知识点——数组指针

在内存中的具体存储如下图所示:

手绘知识点——数组指针

由结果可知,我们输出数组元素可以使用下标,如arr[0]、a[0],也可以使用指针,如*(arr+1)、*(a+1),他们输出的结果是一样的

另外一点就是倒数第二行的输出,四个值都是一样的,大家再看代码中的四个输出值,前三个arr , a , &arr[0]很好理解,都指向数组首元素,那最后一个&arr怎么也是这个值呢,大家都知道,如果arr是指针的话,那取其地址所得到就应该是二级指针(这个我们后边会细聊 , 可以理解为指向指针的指针),但输出结果告诉我们,这并非是一个二级指针,二者指向的都是数组首元素,那它俩有啥区别呢?

来看最后一行的输出,我们分别让这哥俩(从形态上来看应该是父子俩)分别往后走一步,看看迈出的步伐是否整齐,结果显而易见,二者腿并不一样长,一个走了四个字节,指向了下一个数组元素,另一个直接一步跨出了整个数组,并且从地址来看恰巧是数组末尾元素的下一个位置(如手工画图所示),到这里大家应该就明白了,arr+1相当于从数组首元素开始前进一个数组元素大小的距离,而&arr+1则从数组首元素开始前进一个数组大小的距离,可以简单理解成 : 前者的arr代表数组首元素的地址,后者的&arr代表整个数组的地址(还可以这么想 , 我们都知道arr是常量 , 那么它就不能取地址了 , 既然可以输出&arr并没有报错 , 那么此时的arr已经不是之前的arr了 , 这是典型的一词多义啊),咋样,是不是已经凌乱了,有没有一种自由飞翔的感觉~~~

还有比较重要的一个易混点就是数组指针的取值和自增自减的联合运用,我们输出一下数组指针a的几个值:

printf("%d\n", *a);
printf("%d,%d,%d,%d,%d\n", *a,*a++, *++a, (*a)++, ++*a);
           

大家可以先自己算一下,你们认为正确的输出应该是怎样的~~~

看结果:

手绘知识点——数组指针

如何,是不是和你们算得不一样……这一点大家不要纠结了,我的结果也和它不一样 , 这一块我查了很多资料,百思不得其解,很多人都说printf函数的操作原理是从右向左计算,然后从左向右输出,按无论怎能来都不能解释这个结果,最后在论坛看到一位朋友的解释,感觉很有道理,直接截图了:

手绘知识点——数组指针

综上也就是说罪魁祸首就是编译器,它们的解析不尽相同,从而导致输出结果的不同。。。

最后再说几点吧:

1.我们定义数组指针是为了方便对数组元素进行一系类操作,毕竟数组名是个常量,没有变量来的方便;

2.计算数组大小时不可用sizeof(a),我们之前说过sizeof(xx)得到的是xx本身的大小,与其指向的类型是没有关系的,那这个结果就不准确了(一般指针都占据4字节大小)

3.我们定义数组指针时可以直接这样定义:int   arr[]={XXX,XXX},*a=arr ; 这样避免犯之前的错误,使得数组指针指向的类型与数组元素类型不一致(如int   arr[]={XXX,XXX} ;    double  *a=arr ;)

4.上边提到的sizeof , ++ 和*都是操作符 , 了解其优先级有助于对具体操作的理解 , 也为后续的讨论打下坚实的基础 :

手绘知识点——数组指针

—如果本篇内容对你有一点点帮助,请点个赞或者收藏关注一下,让我们一起努力—

——————————————————   分割线   —————————————————————

2019.10.14更新

最近几篇手绘知识点--指针系列文章阅读量差异较大,比如第二篇耗费大量精力最终木有过百,心凉啊。。。

刚创建了一个公众号,一开始的文章是和csdn博客同步的,后续会着重来做这一块,比如发布第一手信息,抽个奖啥的,希望各位小伙伴支持一下,加个关注,如果能帮忙宣传一下就更完美了,爱你们,还是那句话,让我们一起努力,共同进步~

公众号为“非著名IT表演艺术家”,比较中二的名字,就是灵光一闪,然后这个名字就冒出来了……

大家也可以扫码关注,拜托了:

手绘知识点——数组指针

继续阅读