我理解的指针与数组 — 01

前言:
单纯的数组很容易理解,单纯的指针应用起来也不难,但是数组和指针结合起来之后往往会给新手致命一击。每次被那些角度刁钻的习题或者面试题蹂躏之后,不禁会思考人生,这还是我认识的数组和指针吗?然后对着别人的讲解答案草草一看,呦呵原来是这样,貌似也没那么难,然后下次在遇到类似的问题还会悲催的掉进同一个坑里。
其实很多思维活跃,好奇心重的小伙伴往往会在初学的时候都会“脑洞大开”,掉进一些常人都踩不到的坑里,这时候如果是一些一知半解的人企图拉他出来,往往会被带进去跟着一起蒙圈。这个时候,一定不要放弃,人生苦短,不要每次都掉到同一个坑里。
我曾经天真地以为,我对数组和指针的掌握差不多了,现在想来也是少不更事。终于,今天下定决心,要把那些我经常踩的坑都填上!作为一个学习笔记,在写下本文之前我也阅读了很多大佬的文章,但是能让豁然开朗的并不存在,而且内容同质化的问题相当严重。痛定思痛,我总结了一套可以说服自己的理论,可能其中有很多缺陷与不足,敬请指正!

理论水平有限,基础知识不多赘述,下面直入正题。
( 以下代码编译环境均为win32–vs2017 )


1.数组与数组元素
数组是相同类型的变量的有序集合,排列在一块连续的内存空间。在内存中并不存在多维数组,多维数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组。但是数组的元素该怎么理解呢?
其实这个问题很简单,一维数组的元素为变量,二维数组的元素为一维数组,以此类推。但是我们在此还是要借助vs的调试功能让大家看一下数组的内存地址分布以及变量类型,这个有利于我们后边的深入了解和探索。

这里我们直接记录为
结论A:一维数组的元素为变量,二维数组的元素为一维数组,以此类推。

示例一:数组与数组元素

int main() {    
    //代码炒鸡简单
    int a[2] = { 1,2 };
    int b[2][2] = { 1,2,3,4 };
    int c[2][2][2] = { 1,2,3,4,5,6,7,8 };
    system("pause");
}

这里写图片描述
2.数组中的&a[0] , a , &a的区别
单纯就一维数组很论,这是一个很简单的话题,我们先来热热身,在大脑中回简单回忆以下,便于循序渐进的跟进后续内容。
首先大多数人都知道,他们的地址相同,这是肯定的,就内存角度来看他们的地址都是数组所申请的这段连续空间的起始地址。但是你能一下子说清楚他们在具体应用时的区别吗?

示例一:一维数组回顾

int main() {    
    //代码很简单,关键是看图
    int a[4] = { 1,2,3,4 };
    system("pause");
}

这里写图片描述
仔细观察的小伙伴会发现,这一堆的变量有三种类型,int[4] , int* , int[4]*,如果你没有迟疑直接回去看图中的信息了,我还有比要告诉你,他们中间的a是一个量!
2.1 分析:
&a[0]的类型是int*,a[0]是int型数组中第一个元素,&a[0]是一个int型针,&a[0]+1 也是一个int 型指针,显而易见,其步长为 4 字节;

&a 的类型是 int[4] *,是一个数组指针,这个我们后边会做分析。&a+1也是一个数组指针,其步长为16字节,也就是整个数组的内存空间大小,其内部存储的值全部是乱码,已经越界。这里我们就需要思考一下了,步长是16字节?Why?

a 的类型同样是int [4],但是 a+1 的类型却是 int *,步长为4字节,Why?除了那些个好奇宝宝,其他的同学之前注意到过这个问题吗?

2.2 猜想:
B:数组名前加&取地址后,就可以得到一个数组指针。
C:数组名参与运算后就会退化为指针。

2.3 求证与总结:

int main() {    
    //代码超简单,求证很复杂
    int a[2][2] = {1,2,3,4};
    system("pause");
}

2.3.1猜想B求证
废话不多,让图说话:
这里写图片描述
int a[2][2] = { 1,2,3,4 };
a 是一个二维数组,其元素a[0]是一个有2个int型元素的一维数组。

&a[0] 是对一个有2个int型元素的一维数组取地址,其类型为int[2]*,是一个指向该数组本身类型的数组指针。

&a 是对一个2行2列4个int元素的一维数组取地址 ,其类型为 int[2][2]*,也是一个指向该数组本身类型的数组指针。
结论:
B:数组名加&取地址后,就会成为指向该数组本身的类型的数组指针。

2.3.2猜想A求证
还是直接让图说话
这里写图片描述
int a[2][2] = { 1,2,3,4 };
a[0]作为一个一维数组,做+运算后,退化为 int*指针。
a 作为二维数组,做+运算后,退化为 int[2]* 指针。
结论:
C:数组名参与+运算后就会退化为指向该数组元素类型的指针。

以上两个猜想我都用三维数组也做过尝试,其实这就些都是我们无意间发现的一些C语言的规律,但是如果不仔细深究,很难严谨的表达出来。我煞有介事的称之为结论A,B,其实他们可能根本算不上什么结论,但是有助于我们理解和记忆,如有失误,留言评论。

3.数组名a与 [] 和 *
这是一个很简单的知识点,大多系统学习过指针和数组的人都应该知道。* a 解引用系统为 a 这个指针存储的地址分配的空间。int*a,解引用得到一个int型的数值。如果是int(*a)[2],这样的数组指针,解引用就得到一个元素个数为2的int型一维数组,而不是一个值。那么a[i] 呢?
对于一维数组 int a[i],a[i] 等价于 *(a+i),取出的是数组中第 i-1个元素,为一个int 型的值。
对于二维数组 int a[i][j] ,a[i]也等价于 (a+i),取出的是数组中第 i-1个元素,为一个元素个数为 j 的int型数组。a[i][j] 等价于* (* (a+i)+j),取出的就是二维数组中,第 i 行第 j 列的 int型元素。
所以说,[] 可以理解经过封装的指针,简化了多维数组的取值,使二维数组 * (* (a+i)+j) 这种偏底层的索引方式,更加简洁明了的展现给大家。
4. 总结
现在,我们回顾以下我们在这篇文章探讨所得的答案:

A:一维数组的元素为变量,二维数组的元素为一维数组,以此类推。
B:数组名加&取地址后,就会成为指向该数组本身的类型的数组指针。
C:数组名参与+运算后就会退化为指向该数组元素类型的指针。
其实C结论是有问题的,还需要进一步的修改,大家可以思考一下。然后我们再抛出一个问题,数组名a 本身是什么类型?

猜你喜欢

转载自blog.csdn.net/wysnkyd/article/details/82085732