想要清楚的知道指针和数组的本质区别还有处理方式,感觉绕不开看编译器是如何处理的了,有时间可以上知乎搜搜有什么编译类的书籍,或者找个懂编译的大神,在网上看到有一篇从编译器角度剖析数组名的,有所帮助,虽然没有说到足够深入让我理解指针和数组真正的本质是什么:
http://blog.chinaunix.net/uid-27004869-id-3301282.html
本文只是稍微说明一下自己的猜测和理解,可能存在有错的地方,主要还是觉得理解的不够深刻,有时间希望看看编译类的书籍。这个问题真的困扰我很久了,希望大神可以解释。
对于指针和数组名
指针和数组名虽然用法上很多时候是一样的,但其实是两个东西,数组更像是一种数据结构,但是可以转化成指针类型
很多时候数组名可以转化成指针类型让人不好理解,如下
char* argv[];
char* *p=argv;
为什么argv是char**
类型的呢?
从char* argv[]
去分析, 第一步一定要去分析类型!分析类型很简单,不是数组就是指针!argv与[]结合是一个数组,然后存储的数据类型是char*
,所以可以确定argv
是一个指向char*
类型的指针。
上面特地写成了char* *p
这种形式,就是想说明,p是一个指针,指向了char*
类型的数据(这就和数组名一样了)。
所以如何理解变量名的类型呢? 无论是多维数组还是指针,只有进行第一个结合*
或者[]
说明变量是数组还是指针。
比如,对于三维四维指针,都可以这样阅读,比如说char****p
,p
是一个指针,指向了char***
,而不用读成指针的指针的指针的指针…
而对于多维数组,比如说int a[2][2]
,可以这么理解,首先a[2]说明是一个数组,数组中存储的类型是int [2]
(编译器把它处理成了存储数组的首地址),所以是一个指向数组的指针,即int(*)[]。
数组名强制转化成不匹配的指针类型
就像是double类型强转成int类型一样,一个本来应该是int(*)[]
类型的指针,转化成了int**
,如下
int a[2][3]={{1,2,3},{4,5,6}};
int **p=(int**)a;
这个时候p的值和a的值是一模一样的
cout <<p<<endl;
cout <<a<<endl;//两者完全相等
前面提及过了数组更像是一种数据结构,记录了该数组的维度,各个维度的上下界,还重载了*(解引用) +(加)等运算符,一旦完全转化成指针之后,就是一个指针,+
以及*
都没有重载成数组的形式,而是直接+4,没有维度信息了
cout<<*(p+1)<<endl;//结果是00000002,cout还把它当成一个地址来处理,实际上就是&a[0][0]这个地址偏移+1之后中的内容。
cout<<*(a+1)<<endl;//结果是&a[1][0],a是一个数组名,对+以及*都做了重载。
错误的用法,比如说想输出a[1][2]
cout<<*(*(p+1)+2)<<endl; //由于指针对+和*进行数组形式重载,所以该句等价于(*(a[0][1])+2),故而出错
cout<<*(*(a+1)+2)<<endl; //数组名对+和*进行了数组形式重载,所以该句等价于a[1][2]