xiangwangmeili

搞清楚数组和指针

1
阅读(46) 评论(0)

首先来看看几个例子

        int *p[a]; //指针数组

        int (*p)[b]; //数组指针

        int *function(int a);// 指针函数

        int (*p) (int a); //函数指针

        int (*p[a])(int a); //函数指针数组

在叙述数组和指针的关系之前,先琢磨一下上面的五行代码,看你是否能一眼就看出来它们的确切含义。首先很关键的一点就是,我们要知道“ * ”和“[ ]”的优先级,也就是它们的结合顺序,[]优先级大于*,故数组指针声明时要加()。

第一个表达式int *p[a],很明显“[ ]”比“ * ”的优先级要高,如果没有括号,则p优先结合[a],这就说明此表达式为一个数组,*则自动和int结合,为一个int型指针。那么第一个表达式就表示在一个数组p中存放着a个int型指针变量,也就是说这是一个指针的数组,简称指针数组。

第二个表达式int (*p)[b],()存在,则优先级最高,说明p为一个指针,是什么类型的指针呢?不要着急,接着看。剩下的int和[b]自动结合,很容易理解int[b]这是一个整型数组,那么(*p)就可以当做一个整体看成是这个数组的名称。这下就可以看出p是一个指向整型数组的指针变量,也就是说这个表达式是一个数组的指针,简称数组指针。

第三个表达式int *function(int a),首先需要有这么一个认识:表达式后面跟括号参数,那基本上可以判定为和函数有关了(目前暂时还没遇到不是这样的,如有请在评论区告知,谢谢),其实把握住这一点也不难理解,基本上就和第一个表达式同样的判别方法了。function优先和后面的(int a)结合,表示一个函数,它的参数为int a,而前面的*自动和int结合为int*,这样一来(int*)就可以当成整体看成函数的返回值了。也就是说这个表达式表示的是一个function函数,参数为int a,返回值为int*,如果写成这样(int *) function(int a),可能大部分人都认识了。总的来说这是一个指针的函数(注意返回值是什么我们就读作什么函数,类似于把返回值为整型的函数读作整型函数一样),本质是个函数,简称指针函数。

第四个表达式int (*p) (int a),和上述第二个表达式类似,只是后面的数组大小部分换成了函数的参数。(int a)表示函数的参数,前面的int表示函数的返回值为一个整型值,*和p结合表示P是一个指针,那么这个指针是什么类型的呢,也就是说指向什么东西呢?这是一个p指针,指向一个参数为int a、返回值为int型的函数。对的,你没看错这个函数没有函数名(函数名表示的是函数的入口地址,类似于函数的指针),但它有一个P指针。所以这个表达式是一个指向函数的指针,简称函数指针。

第五个表达式int (*p[a])(int a),看见它先不要害怕。来,一步步分析,和第四个表达式很相似,表达式后面带括号证明这个表达式里面有函数成份。(int a )表示函数的参数,表达式中第一个int表示函数的返回值为整型。再来看看(*p[a]),和前面的分析方法相同,[ ]优先级高先结合,表示这是一个P数组,数组里面存放的什么东西呢?[a]和p结合,*和剩余部分结合,表示函数的指针,那么P数组里面存放的就是函数的指针。注意存放的是一个参数为整型、返回值为整型的函数的指针。所以第五个表达式表示,一个p数组里面存放着一组参数为int型、返回值为int型的函数的指针,简称函数指针数组。

读以上五个名词的时候注意把握一个要点,那个单词在最后,说明本质就是什么,数组指针也就是数组的指针,表明本质是指针;函数指针数组也就是函数的指针的数组,本质表明是一个数组。

来个稍微复杂的:指向指针函数的函数指针数组,简称指针函数函数指针数组

int *(*funcp[N])(int )

好了,搞明白以上几个定义,接下来我们说说数组和指针的关系。都知道数组名在一般情况下可以理解为一个指针。例如int arry[N]表示一个整形数组,int *p表示一个整形指针。则这个arry是可以直接赋给p的,如下图:

ss.png


注意在这里把数组名赋给指针变量的时候对数组名用不用取地址符&都是可以的。以后就可以采用*(p+1)的方式来访问数组arry了。用指针变量直接指向数组元素,不必每次都计算地址,可以大大提高执行效率。


上面是一维数组和指针的关系,接下来看看二维数组和指针的关系(这里的指针包含一级指针和二级指针,我们分别讲解)。二维数组在内存里面其实也是按照一维数组的方式顺序存放的,可以想象为第二行的首接第一行的尾放置。首先要知道二维数组的数组名是个什么玩意,二维数组的数组名其实就是二维数组的首地址,只不过这个首地址是个行地址。也就是说二维数组的数组名如果当成是和一维数组名一样的等级时,那么这个数组里面的每个元素则是一列元素罢了。定义二维数组为int a[3][4],那么a[0],a[1],a[2],可以理解为一位数组名,代表的是这个二维数组中每一行元素的首地址,即a[0]就代表的是&a[0][0],a[1]就代表的是&a[1][0]。那么自然地&a[0][1]就可以用a[0]+1来表示。因此我们可以得出a[1]等价于*(a + 1),&a[0][1]等价于a[0] +1 和*(a + 0) + 1,那么*(a[i]+j)和*(*(a+i) + j)就表示二维数组元素a[i][j]的值。


首先要说明一点,a+1是二维数组a中序号为1的行的首地址,而*(a+1)为序号为1的行的首元素的地址,可以看成*(a+1)+0来理解。而并不是a+1单元中的内容,因为a+1并不是一个变量的地址,而是一行元素的首地址。*(a+1)就是a[1],而a[1]是一维数组的数组名,也是地址,指向a[1][0]。因此a[1]和*(a+1)都是二维数组中地址的表示方法。在这里,我们可以把*看做一个降维符号,而不是取值符号。可以看到在指向行的指针前面加一个*就把指针转化为了指向列的指针。反之在指向列元素的指针前面加&就变成了行指针。

在这里要特别注意,初学者会有一个误区,认为一级指针int*p对应一维数组int a[2],即 p = a;二级指针int**pp对应二维数组int a[2][3],即pp = a;其实非也。二维数组的对应是数组指针,而不是二级指针。不能用二级指针直接指向二维数组。第一点是不可以,第二点是不需要。如果需要指针每次移动的单位为二维数组中的一行内存,则需定义一个数组指针,这个数组指针指向的数组大小即就是二维数组的第二个下标,也就是每行元素的长度列。