c语言最有意思最灵活的就是指针了。之前写过一些关于指针的基础,但是关于数组和指针涉及的不是很多,这里主要再讲一下数组和指针。
一、指针数组:
指针数组,顾名思义,即数组中存储的数组元素都是指针变量,定义的形式如下:
变量类型 * 数组名[];(例:int * a[10],定义了是个整型的指针数组)。
对于普通的数组来说,数组名可以看作是一个一级指针常量,因此,该数组的数组名可以认为是一个二级指针,因为它指向的数据的类型是指针类型,即指向指针的指针。所以定义一个与之同类型的指针形式应该如下:
变量类型 **变量;(例:int ** p;p = a;这样就可以用p来表示a了)
同样,如果要将一个指针数组传到函数中,函数的形参也应该写成这样的形式。
二、指向数组的指针:
数组为a[10],那么指向数组的指针就是(&a),她就是指向数组的指针,既然说是指向数组,那么她就是指向整个数组,她+1跨过的长度就是整个数组的空间长度,对于这个例子,跨过的空间长度是40个字节。
指向数组的指针一般都是用在二维数组中,定义的形式是:
数据类型 (*p)[N];(例:int a[5][10];int (*p)[10];p = a;)
同样,传参数的时候就要用这个形式来定义一个形参,在传实参的时候,只要将数组名a传进去就可以了,作为形参传递二维数组还可以用*p[][10]的形式来传递这个二维数组。
实际上,这样定义的p是一个特殊的二维指针,为什么是二维指针?因为它可以解引用两次,(*(*(p+i)))[10]就是二维数组对应的a[i][10]。
这样就引出了指针和数组的等效公式,形式如下:
a[i] 等同于 *(p+i) 等同于p[i]等同于*(a+i)
a[i][j] 等同于 (*(*(p+i)))[j]等同于 (*(*(a+i)))[j]等同于p[i][j]
上述这个等效公式是完全等价的。
二维数组a[i][j]中的a[i]可以看作是a[i][j]这一行的一维数组的数组名,所以a[i]也可以看作是指针。
三、数组名的探究
当数组名a作为右值时,a代表的实际上是指向数组的首地址的指针,所以她的类型是&a[0],她是一个常量,即她的类型为数组元素的*类型。如:int a[10],那么a的类型为int*型。但是以下几种情况比较特殊:
1、取地址&a
假设一个数组为int a[10],打印的时候,(&a)实际上也是一个指针,可以将它看作指向整个数组的指针,那么这个指针的类型就是int (*p)[10]型。因为a代表的是整个指针,所以对她进行取地址操作的时候,指向的是整个数组,所以(&a)+1就是就是溢出数组的范围了。所以这就好理解了为什么二维指针中的a的类型是int (*p)[10]型了,二维数组名a可以看作是(a[i])的一维数组的数组名,所以a可以认为等同于(&a[i]),(&a[i])就是代表了整个a[i]数组的长度,也就是二维指针的列数,例子中是10个int型,所以数组a的类型为10个int型的长度,所以她的类型就是int (*p)[10]型。
2、sizeof(a)
用sizeof求a时,a代表的是整个指针,所以a的大小是10*sizeof(int),占了四十个字节
3、a+i
(a+i)的意义与&a[i]的意义相同,所以用sizeof(a+i)求出来是4个字节(32位系统),这时候她的用法就可以当作一个指针常量来使用。所以一旦数组名参与了运算,那么她就是一个指针常量,除了作为左值,其他用法和指针变量几乎一样。所以,a+0,&a[0]和a作为右值时候的意义都是一样的。
4、一些常识
在数值上,a,&a,&a[0]都是一样的,但是意义上,a,&a[0]的意义是一样的,都是首元素的地址,但是&a的意义是指向数组的指针,她的步长是整个数组的长度。
四、小节
在指针的使用中,一定要注意指针类型的赋值一定要类型匹配(相同),因为如果类型不匹配,程序一般只会报警告而不会报错,但是在使用时候会经常出现问题。这也就是指针变量所占的空间都是4个字节(32位系统),但是却有类型的原因,她的类型就是定了该指针的步长,就是p+1所跨过的字节数。