张龙波
(江苏师范大学科文学院 江苏 徐州 221116)
在讨论C中普通指针与一维、二维数组的关系之前,我们的预备知识如下:
(1)一维数组a[i]中的元素a[m](m≤i-1)的地址的2种表示方法:&a[m],a+m;
(2)一维数组a[i]中的元素a[m](m≤i-1)的值的2种表示方法:a[m],*(a+m);
(3)二维数组a[i][j]的第m行第n列(m≤i-1,n≤j-1)元素a[m][n]的地址的3种表示方法:*(a+m)+n,a[m]+n,&a[m][n];
(4)二维数组a[i][j]的第m行第n列(m≤i-1,n≤j-1)元素a[m][n]的值的3种表示方法:*(*(a+m)+n),*(a[m]+n),a[m][n]。
我们已知:普通的指针变量只能指向相同数据类型的普通变量,a[0]是整型变量,p是整型指针变量,故二者不可以使用“p=a[0]”进行直接指向。可以采取以下两种方法进行指向:
(1)p=a;(数组名a代表一个指针常量,它指向第一个元素对象a[0])
(2)p=&a[0];(在整型变量a[0]的左侧加“取地址运算符”即可获取其内存单元格地址,故可以直接让指针变量p指向普通变量a[0]对应的内存单元)
初学者容易写出这样的代码指向:p=a;(这样写是错误的,因为尽管a是一个常指针(即指针常量),但是a是指向“第一个包含4个元素对象的、所谓的一维数组元素a[0]的”)。因此建议采取以下三种方法进行指向:
(1)p=*a;(这样写才正确;因为*a是对a这个常指针“取内容”的,*a的值是a[0];因为这个a[0]它也是一个常指针,但是它是指向其内部的4个元素对象的第一个元素对象a[0][0]的,即a[0]的值与&a[0][0]的值等同;所以不难得出下面的两种方法)
(2)p=a[0];
(3)p=&a[0][0];(直接让指针变量p去指向普通变量a[0][0])
(1)二维数组a[i][j]中,“a+m”和“&a[m]”(m≤i-1)代表的含义又是什么呢?我们发现它们的作用是相同的,都是用来指向二维数组a的第m行所有元素对象的;
(2)二维数组a[i][j]中,“*(a+m)+n”和“a[m]+n”(m≤i-1,n≤j-1)指向的又是什么呢?我们发现它们的作用也是相同的,即指向了二维数组对应矩阵中的第m行第n列的元素对象a[m][n];
基于以上分析得出结论:使用普通指针变量指向二维数组元素a[m][n]的通用公式是:p=a[m]+n或p=*(a+m)+n或p=&a[m][n]。
(1)问题的提出:如何定义一个指针p,将其指向具有6个元素的一维数组a呢?它们之间是如何指向及相互联系的呢?具体探讨的实例代码行如下:
第1行:inta[6]={11,13,15,17,19,21};(定义一个包含有6个元素的一维整型数组a)
第2行:int(*p)[6];(必须定义一个数组指针,这样才可以指向一维数组;p的类型是:int*[6],所以p是一个专门指向含有6个元素的一维数组的指针变量)
第3行:p=&a;(这是可以采取的方法,作用是将数组名a所在的内存地址赋值给数组指针p,即p指向含有6个元素的一维数组a)
第4行:printf("%p ",&a[0]);(&a[0]是用来指向a[0]这个元素对象的地址)
第5行:printf("%p ",&a);(这个结果和上一行结果一样,但含义不同;&a是用来指向整个一维数组a的)
第6行:printf("%d ",*((*p)+3));(*p完全等价于数组名a;输出表列“*((*p)+3)”即变成“*(a+i)”;“*(a+i)”完全等价于a[i],故打印出第4个数组元素的值17)
第7行:printf("%d ",(*p)[3]);(*p完全等价于数组名a;所以输出表列完全等价于a[3],故打印出17)
第8行:p=p+1;(因为p是一个“只能指向具有6个元素的一维数组的数组指针”,此行代码的作用就是:数组指针变量p一下子完美跳过了整个的一维数组)
第9行:printf("%p ",p);(p一下子移动了24个内存单元格,所以打印出的内存地址比初始地址大了18H;此行代码等价于printf("%p ",&a+1))
(2)问题的提出:若想定义一个指针p,需要将其指向二维数组a[3][2]的某行时(如a的第2行),需要将该指针定义成什么类型的指针?如何指向?
a的第2行地址代表的本质是:一个包含2个元素对象的一维数组。因此我们可以采取如下的方法:
第1行:inta[3][2]={11,13,15,17,19,21};
第2行:int(*p)[2];(定义一个数组指针变量p,这个p只能指向包含2个元素对象的一维数组)
第3行:p=a+2;(代表了p指向了二维数组a的第2行)
第4行:printf("%d ",*(*p));(打印出a[2][0]这个元素,结果为19)
第5行:printf("%d ",*(*p+1));(打印出a[2][1]这个元素,结果为21)
(3)指针变量在什么情况下可以做相加或相减的运算?
一般情况下,相同类型的指针变量之间只能做相减运算,相加运算没有意义;同时要求这几个指针变量必须同时指向同一数组中的元素对象的时候!如:p1和p2是完全相同类型的指针变量,则“p2-p1”的本质含义及结果是:“(p2-p1)/(指向的元素对象的字节数)”。(注意:上方所说的元素对象有可能是一个数据,也有可能是一行数据,请务必理解)。
(4)一维数组a的元素a[i]可以表示成*(a+i)吗?二维数组a的元素a[i]可以表示成*(a+i)吗?都可以。前者的*(a+i)表示a[i]这个一维数组元素的值;而后者的*(a+i)等价于*(a+i)+0;代表的是a[i][0]这个数组元素的地址。(即第i行数组元素的地址)。
(5)函数调用过程中的实参数组名和形参数组名均可以被赋值吗?前者不可以,因为C编译器会把实参数组名认识是一个常量指针,常量指针是不可以再次被赋值的;后者却可以,因为C编译器会把形参数组名认识是一个指针变量,指针变量当然可以再次被赋值。这两点请读者务必注意。
(6)某二维数组a[m][n]的数组元素a[i][j]在数组中的相对位置(相对于首元素a[0][0]的地址)的计算公式是?通过对二维数组对应矩阵数据的分析,我们不能得出a[i][j]的地址是:&a[0][0]+(i*n+j)。(n为二维数组的列数)。
(1)对于一维数组a[6],&a、a和*a的区别是?
&a:这是个指针常量,它指向了整个的包含6个元素的一维数组a;
a:这也是个指针常量,它指向了一维数组a中的第1个元素a[0];(即a等价于&a[0])
*a:这是一个普通数据,代表的是数组a的第一个元素a[0]的值。
(2)对于一维数组a[6],&a+1、a+1和*a+1的含义分别是?
&a+1:依然是一个指针常量,它指向了整个的一维数组a所有元素之后的下一个内存单元格;
a+1:依然是个指针常量,它指向了一维数组a中的第2个元素a[1];
*a+1:依然是一个普通数据,代表的是数组第一个元素a[0]的值再加上一个普通十进制数字1。
(3)对于二维数组a[3][4],&a、a和*a的区别是?
&a:这是一个指针常量,它指向了整个的包含了12个元素的二维数组a;
a:这也是一个指针常量,它指向了第一个元素对象(即二维数组的第1个元素对象是一维数组a[0]),所以a是一个行指针常量,指向了二维数组的第一行(包含4个元素);
*a:依然是一个指针常量,它等价于*(a+0)+0;它指向的是“第0行第0列这个元素对象”。
(4)对于二维数组a[3][4],&a+1、a+1和*a+1的含义分别是?
&a+1:依然是一个指针常量,它指向了整个的二维数组a(包含12个元素)后边的一个内存单元格;
a+1:依然是一个指针常量,它指向了第2个元素对象(二维数组的第2个元素对象是一维数组a[1]),所以a+1也是一个行指针常量,指向了第2行;
*a+1:依然是一个指针常量,它等价于*(a+0)+1;它又等价于a[0]+1;所以它指向的是“第0行第1列这个元素对象”。