论C语言中指针和数组关系

2022-09-10

指针是C语言中的一个重要的概念, 不掌握指针就没有掌握C的精华。其中数组和指针的关系, 特别是多维数组和指针的关系更容易让人混淆。本文通过几条简单的结论, 来阐述两者的关系。

1 预备概念和结论

本文把指向具体元素的指针称为点指针;把指向包含n个具体元素一维数组的指针称为行指针。

int*p表示声明p是指向具体元素的指针, 为点的指针。

int (*pointer) [4]表示声明pointer是一个含4个具体元素一维数组的指针, 为行指针。

在C语言中, 地址可以认为是一个指针, 见下例:

运行结果如下:

&a是a的地址, aPtr是指向a变量的指针, 两者输出结果一致;&a+1和aPtr+1输出结果一致;*&a和*aPtr输出结果一致。

从结果看, 如果把&a视为一个指针, 一个指向变量a的指针, 那么很好解释&a和aPtr等价, &a+1和aPtr+1等价, *&a和a P t r等价, 因此他们输出结果分别一致。

地址就是指针, 该结论是理解数组和指针关系的基础。

2 一维数组

假设有:

(1) 数组名称就是地址和指针。

一维数组名称a r r a y代表数组中首元素 (array[0]) 的地址, p=array是把首元素地址赋给p。根据“地址就是指针”, array实际上就是一个指针, 一个指向首元素的指针, p=array也可以认为是把一个指针赋给另一个指针, 使得p跟array一样指向首元素。

p=array和p=&array[0]等价。&array[0]表示array[0]的地址, p=&array[0]是把首元素array[0]的地址赋给指针p, 使得p指向首元素。根据“地址就是指针”, &array[0]实际就是一个指针, 一个指向首元素的指针, 因此, p=&array[0]也可以认为是把一个指针赋给另一个指针, 使得p跟&array[0]一样指向首元素。因此两者是等价的。

(2) 一维数组名称是点指针。

一维数组名称a r r a y表示一个指针常量, 该指针指向首元素 (第0号元素, 为具体的元素) , 因此一维数组名称a r r a y是点指针。因为array为点指针, 那么array+1指向的是数组中第1号元素。

(3) *array跟array[0]等价, * (array+1) 和array[1]等价。

由于a r r a y为指向首元素的点指针, 那么*array表示该指针指向的元素 (首元素) , 而在一维数组中, array[0]表示首元素, 因此*array跟array[0]等价。

由于a r r a y表示指向首元素的点指针, 那么array+1指向下一个点 (即下一个具体元素) 。因此, * (array+1) 和array[1]等价, 其他依次类推。

3 二维数组

假设定义一个二维数组如下:

int a[3][4]={{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}。

(1) 把二维数组a视为一维数组, 其元素为a[0], a[1]和a[2], 其中a[0], a[1]和a[2]分别是包含4个具体元素的一维数组 (见图1) 。这里面包含两个信息。

(1) 把a作为一维数组来理解, 那么名称a就是一个指针, 指针a指向的是a[0], a+1指向的是a[1], a+2指向的是a[2]。由于a[0], a[1]和a[2]并不是具体的元素, 而是包含4个具体元素的一维数组。因此a和a+1, a+2分别是行指针。

(2) a[0], a[1]和a[2]并不是具体的元素, 而是包含4个具体元素的一维数组。因此把a[0], a[1]和a[2]作为一维数组名称来使用。

一维数组名称就是一个指针, 其指向该数组的首元素。本文把指向具体元素的指针称为点的指针, 因此a[0], a[1], a[2]就是指向该数组中首元素的指针, 为点的指针。

(2) a[0]+1指向的是第0行第1列元素, a[1]+2指向的是第1行第2列元素。

由于a[0]表示指向第0行数组的首元素 (第0列元素) , 为点的指针, a[0]+1表示把该指针移动1个位置, 即指向该数组中的第1列元素, 即指向的是第0行第1列元素;同样, 由于a[1]表示指向第1行数组的首元素 (第0列元素) , 为点的指针, a[1]+2表示把该指针移动2个位置, 即指向该数组中的第2列元素, 即指向的是第1行第2列元素;

点指针为具体指向某个元素的指针, * (a[0]+2) 表示第0行第2列元素。

(3) a[1]+2和* (a+1) +2等价。

把a看成是一个含有3个元素的一维数组。采用下标法访问元素, 即a[0], a[1]和a[2];采用指针法访问元素, 如*a, * (a+1) 和* (a+2) 等。因此a[1]和* (a+1) 等价, 所以a[1]+2和* (a+1) +2等价。

a[1]+2和* (a+1) +2都表示指向第1行第2列元素的指针, 因此* (a[1]+2) 和* (* (a+1) +2) 均表示该指针指向的元素。

(4) *a和a输出值一样, 类型不一样。

测试如下的程序, 观察*a和a的输出值:

测试表明*a和a输出的值一样。a代表0行首地址, *a其代表第0行第0列的地址, 因此两者的输出值是一样的, 但是两者的类型不一样。

a是行指针, 其代表0行首地址。a[0]和*a是点的指针 (a[0]和*a等价) , 其代表第0行第0列的地址。因此a和*a输出的值一样, 但是前者是行指针, 后者是点指针。

进一步测试如下程序, 该例的目的是输入二维数组a的行和列的下标, 把相应位置的元素输出:

发现以上程序通不过编译, 错误在于指针类型不匹配。p是点指针, 而a是二维数组名, 表示一个行指针常量, 显然把个行指针常量赋给一个点指针, 类型上不一致。因此, 修改如下:

发现以上程序通过编译, 功能满足要求。int (*p) [4]表示p为一个指针变量, 它指向包含4个整型元素的一维数组, 为行指针, 因此p和a类型一致。

如果采用点指针来完成上述程序的功能, 可以把程序修改为:

*a其代表第0行第0列的地址, 根据地址就是指针, *a就是第0行第0列的元素的指针, 为点指针。p=*a实现把p指向第0行第0列的元素, 亦为点指针。本例子是采用点移动的方法, 找到相应行和列的元素。

同样, 函数调用就是把实参赋给形参。如果指针作为函数参数, 必须分清形参和实参的类型并判别两者类型是否一致。数组名的本质就是指针, 因此数组名作函数参数, 实质就是指针作为函数参数, 同样遵守以上的规则。

4 结语

综上所述, 在数组和指针关系中, 有如下三个主要结论。

(1) 地址就是指针。

(2) 数组名称是既表示地址, 又表示指针。一维数组名称是点指针, 二维数组名称是行指针。

(3) 假设有array[n][m], 二维数组array认为是包含array[0], array[1], array[2]…array[n-1]等n个元素的一维数组, 而这n个元素分别是包含了m个具体元素的一维数组, array[0], array[1], array[2]…array[n-1]等可以视为一维数组名称。

摘要:本文创造性地提出了“地址就是指针”, 该结论是理解数组和指针关系的基础;创造性地提出“点指针和行指针”的概念, 这些概念很好区分了多维数组中两种不同类型的指针;在此基础上, 进一步认为“数组名称既表示地址, 又表示指针”“, 二维数组array[n][m], 可以认为是含有array[0], array[1]……array[n-1]等n个元素的一维数组, 而这些元素是包含了m个具体元素的一维数组;一维数组名称, 如array[0], array[1]……array[n-1]是点指针, 二维数组名称array是行指针”。

关键词:C语言,指针,数组

参考文献

[1] (美) 阿霍, 赵建华[译].编译原理 (第2版) [M].机械工业出版社, 2009, 1.

[2] 张素琴.编译原理 (第2版) [M].清华大学出版社, 2005, 2.

[3] 严蔚敏, 吴伟民.数据结构 (C语言版) [M].清华大学出版社, 1997, 4.

[4] 刘景, 周玉龙.高级语言C++程序设计 (第2版) [M].高等教育出版社, 2006, 12.

上一篇:铁道工程技术专业课程设置探讨下一篇:不同混凝剂预处理制革废水实验研究