C语言的指针与多维数组


C语言的指针与多维数组

文章插图
 
指针和多维数组有什么关系?为什么要了解它们的关系?处理多维数组的函数要用到指针,所以在使用这种函数之前,先要更深入地学习指针 。至于第1个问题,我们通过几个示例来回答 。为简化讨论,我们使用较小的数组 。假设有下面的声明:
int zippo[4][2];/* an array of arrays of ints */然后数组名zippo是该数组首元素的地址 。在本例中,zippo的首元素是一个内含两个int值的数组,所以zippo是这个内含两个int值的数组的地址 。下面,我们从指针的属性进一步分析 。
  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同 。而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素(一个整数)的地址(即&zippo[0][0]的值)相同 。简而言之,zippo[0]是一个占用一个int大小对象的地址,而zippo是一个占用两个int大小对象的地址 。由于这个整数和内含两个整数的数组都开始于同一个地址,所以zippo和zippo[0]的值相同 。
  • 给指针或地址加1,其值会增加对应类型大小的数值 。在这方面,zippo和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象只占用一个int大小 。因此,zippo + 1和zippo[0] + 1的值不同 。
  • 解引用一个指针(在指针前使用*运算符)或在数组名后使用带下标的[]运算符,得到引用对象代表的值 。因为zippo[0]是该数组首元素(zippo[0][0])的地址,所以*(zippo[0])表示存储在zippo[0][0]上的值(即一个int类型的值) 。与此类似,*zippo代表该数组首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址 。该值的地址是&zippo[0][0],所以*zippo就是&zippo[0][0] 。对两个表达式应用解引用运算符表明,**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值 。简而言之,zippo是地址的地址,必须解引用两次才能获得原始值 。地址的地址或指针的指针是就是双重间接(double indirection)的例子 。
显然,增加数组维数会增加指针的复杂度 。现在,大部分初学者都开始意识到指针为什么是C语言中最难的部分 。认真思考上述内容,看看是否能用所学的知识解释程序中的程序 。该程序显示了一些地址值和数组的内容zippo1.c 。
/* zippo1.c --zippo info */#include <stdio.h>int main(void){int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };printf("zippo = %p, zippo + 1 = %pn",zippo,zippo + 1);printf("zippo[0] = %p, zippo[0] + 1 = %pn",zippo[0],zippo[0] + 1);printf("*zippo = %p,*zippo + 1 = %pn",*zippo,*zippo + 1);printf("zippo[0][0] = %dn", zippo[0][0]);printf("*zippo[0] = %dn", *zippo[0]);printf("**zippo = %dn", **zippo);printf("zippo[2][1] = %dn", zippo[2][1]);printf("*(*(zippo+2) + 1) = %dn", *(*(zippo+2) + 1));return 0;}下面是我们的系统运行该程序后的输出:
: zippo = 0x7fff26a9a3a0, zippo + 1 = 0x7fff26a9a3a8: zippo[0] = 0x7fff26a9a3a0, zippo[0] + 1 = 0x7fff26a9a3a4: *zippo = 0x7fff26a9a3a0,*zippo + 1 = 0x7fff26a9a3a4: zippo[0][0] = 2:*zippo[0] = 2:**zippo = 2:zippo[2][1] = 3: *(*(zippo+2) + 1) = 3其他系统显示的地址值和地址形式可能不同,但是地址之间的关系与以上输出相同 。该输出显示了二维数组zippo的地址和一维数组zippo[0]的地址相同 。它们的地址都是各自数组首元素的地址,因而与&zippo[0][0]的值也相同 。
尽管如此,它们也有差别 。在我们的系统中,int是4字节 。前面讨论过,zippo[0]指向一个4字节的数据对象 。zippo[0]加1,其值加4(十六进制中,38+4得3c) 。数组名zippo是一个内含2个int类型值的数组的地址,所以zippo指向一个8字节的数据对象 。因此,zippo加1,它所指向的地址加8字节(十六进制中,38+8得40) 。
该程序演示了zippo[0]和*zippo完全相同,实际上确实如此 。然后,对二维数组名解引用两次,得到存储在数组中的值 。使用两个间接运算符(*)或者使用两对方括号([])都能获得该值(还可以使用一个*和一对[],但是我们暂不讨论这么多情况) 。
要特别注意,与zippo[2][1]等价的指针表示法是*(*(zippo+2) + 1) 。看上去比较复杂,应最好能理解 。下面列出了理解该表达式的思路:
C语言的指针与多维数组

文章插图
 
以上分析并不是为了说明用指针表示法(*(*(zippo+2) + 1))代替数组表示法(zippo[2][1]),而是提示读者,如果程序恰巧使用一个指向二维数组的指针,而且要通过该指针获取值时,最好用简单的数组表示法,而不是指针表示法 。


推荐阅读