指针数组
数组指针是指向数组的指针,在C语言中,常用的数组指针为一维数组指针和二维数组指针,下面分别进行介绍。
1、 一维数组指针
数组在内存中占据一段连续的空间,对于一维数组来说,数组名默认保存了数组在内存中的起始地址,而一维数组的第1个元素与一维数组的起始地址是相同的,因此在定义指向数组的指针时,可以直接将数组名赋值给指针变量,也可以取第1个元素的地址赋值给指针变量。
以int类型数组为例,假设有一个int类型的数组,定义如下:
int arr[5]={3,5,4,7,9};
指向数组的指针变量的类型与数组元素的类型是相同的,定义一个指向该数组的指针,示例代码如下所示:
int *p1 = arr; //将数组名arr赋值给指针变量p1
int *p2 = &arr[0]; //取第1个元素的地址赋值给指针变量p2
上述代码中,指针p1与指针p2都指向数组arr。
数组指针可以像数组名一样,使用索引取值法对数组中的元素进行访问,格式如下所示:
p[索引] //索引取值法
例如,通过指针p1访问数组arr的元素,示例代码如下:
p1[0] //获取数组第1个元素3,相当于arr[0]
p1[1] //获取数组第2个元素5,相当于arr[1]
数组指针除了通过索引访问数组元素之外,还可以通过取值运算符“”访问数组元素,例如,通过p1可以访问到数组的第1个元素,如果访问数组后面的元素,如访问第3个元素arr[2],则有以下两种方式。
(1)移动指针,使指针指向arr[2],获取指针指向元素的值,代码如下所示:
p1 = p1+2; //将指针加2,使指针指向arr[2]
*p1; //通过*运算符获取到arr[2]元素
在上述代码中,指针p1从数组首地址后移动了2个步长,指向了数组第3个元素。由于数组是一段连续的内存空间,因此指针可以在这段内存空间上进行加减运算,在执行p1=p1+2之后,指针p1向后移动,从第1个元素指向第3个元素。其内存图解如图1所示。
图1 移动指针p1访问数组元素
(2)不移动指针,通过数组元素指针间的关系运算取值:
*(p1+2) //获取元素arr[2]
上述代码中,指针p1还是指向数组首地址,以指针p1为基准,取后面两个步长处的元素,即arr[2],其内存图解如图2所示。
图2 不移动指针p1访问数组元素
在图2中,指针p1仍旧指向数组起始地址,而在图6-30中,指针p1被移动到了元素4的位置。
当指针指向数组时,指针与整数加减表示指针向后或向前移动整数个元素,同样指针每自增自减一次,表示向后或向前移动一个元素。当有两个指针分别指向数组不同元素时,两个指针还可以进行相减运算,运算结果为两个指针之间的数组元素个数,其内存图解如图3所示。
图3 数组指针相减内存图解
在图3中,指针p1指向数组首元素,指针p2指向数组第4个元素,则执行p2-p1,结果为3,表示两个指针之间相差3个元素。这是因为指针之间的运算单位是步长,其实p1与p2之间的相差12个字节,即相差3个sizeof(int)。
**2、二维数组指针**
二维数组指针的定义要比一维数组复杂一些,定义二维数组指针时需指定列的个数,定义格式如下:
数组元素类型 (*数组指针变量名)[列数];
上述语法格式中,“数组指针变量名”使用了一个括号括起来,这样做是因为“[]”的优先级高于“”,如果不括起来编译器就会将“数组指针变量名”和“[列数]”先进行运算,构成一个元素都是指针类型数据的数组,而不是定义指向数组的指针。
假设定义一个2行3列的二维数组arr,示例代码如下:
int arr[2][3]={{1,2,3},{4,5,6}};
按照上述格式定义指向数组arr的指针,示例代码如下:
int (*p1)[3] = arr; //二维数组名赋值给指针p1
int (*p2)[3] = &arr[0][0]; //取第一个元素的地址赋值给p2
上述代码中,指针p1与指针p2都指向二维数组arr,这与一维数组指针赋值方式是相同的,但二维数组的每一行可以看作一维数组,如图6-24(b)所示,在数组arr中,arr[0]是个一维数组,表示二维数组的第1行,它保存的也是一个地址,这个地址就是二维数组的首地址,因此在定义二维数组指针时,也可以将二维数组的第1行地址赋值给指针,示例代码如下:
int (*p3)[3] = arr[0]; //取第一行地址赋值给 p3
上述代码中,指针p3也是指向二维数组arr,对p1、p2、p3指针执行取值运算,结果都是二维数组的第1个元素。虽然可以通过多种方式定义二维数组指针,但平常使用最多的还是直接使用二维数组名定义二维数组指针。
使用二维数组指针访问数组元素可以通过索引的方式,示例代码如下:
p1[0][0]; //访问第1个元素
除此之外,还可以通过移动指针访问二维数组中的元素,但指针在二维数组中的运算与一维数组不同,在一维数组中,指向数组的指针每加1,指针移动步长等于一个数组元素的大小,而在二维数组中,指针每加1,指针将移动一行。以数组arr为例,若定义了指向数组的指针p,则p初始时指向数组首地址,即数组的第1行元素,若执行p+1,则p将指向数组中的第2行元素,其逻辑结构与内存图解如图4所示。
图4 二维数组指针移动图解
由图4可知,在二维数组arr中,指针p加1,是从第1行移动到了第2行,在内存中,则是从第1个元素移动到了第4个元素,即跳过了一行(3个元素)的距离。综上所述,二维数组指针每加1,就移动1行。
了解了二维数组指针的移动过程,就可以很容易地通过移动二维数组指针访问二维数组中的元素,例如,通过p访问二维数组arr中的第2行第2列的元素,示例代码如下所示:
*(p[1]+1)
*(*(p+1)+1)