求值:int *a[6][3]; int expr=a[5]-a[2];
输出expr=9.
1. 二维数组元素的地址
为了说明问题, 我们定义以下二维数组:
int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
a为二维数组名, 此数组有3行4列, 共12个元素。但也可这样来理解, 数组a由三个元素组成: a[0], a[1], a[2]。而它匀中每个元素又是一个一维数组, 且都含有4个元素 (相当于4列), 例如, a[0]所代表的一维数组所包含的 4 个元素为 a[0][0], a[0][1], a[0][2], a[0][3]。如图5.所示:
┏━━━━┓ ┏━┳━┳━┳━┓
a─→ ┃ a[0] ┃─→┃0 ┃1 ┃2 ┃3 ┃
┣━━━━┫ ┣━╋━╋━╋━┫
┃ a[1] ┃─→┃4 ┃5 ┃6 ┃7 ┃
┣━━━━┫ ┣━╋━╋━╋━┫
┃ a[2] ┃─→┃8 ┃9 ┃10┃11┃
┗━━━━┛ ┗━┻━┻━┻━┛
图5.
但从二维数组的角度来看,a代表二维数组的首地址, 当然也可看成是二维数组第0行的首地址。a+1就代表第1行的首地址, a+2就代表第2行的首地址。 如果此二维数组的首地址为1000, 由于第0行有4个整型元素, 所以a+1为1008, a+2 也就为1016。如图6.所示
a[3][4]
a ┏━┳━┳━┳━┓
(1000)─→┃0 ┃1 ┃2 ┃3 ┃
a+1 ┣━╋━╋━╋━┫
(1008)─→┃4 ┃5 ┃6 ┃7 ┃
a+2 ┣━╋━╋━╋━┫
(1016)─→┃8 ┃9 ┃10┃11┃
┗━┻━┻━┻━┛
图6.
既然我们把a[0], a[1], a[2]看成是一维数组名, 可以认为它们分别代表它们所对应的数组的首地址, 也就是讲, a[0]代表第 0 行中第 0 列元素的地址, 即&a[0][0], a[1]是第1行中第0列元素的地址, 即&a[1][0], 根据地址运算规则, a[0]+1(这里不是地址直接相加,可以看成指针+1,感觉a[0]就是指针,指向第一行)即代表第0行第1列元素的地址, 即&a[0][1], 一般而言, a[i]+j即代表第 i行第j列元素的地址, 即&a[i][j]。
另外, 在二维数组中, 我们还可用指针的形式来表示各元素的地址。如前所述, a[0]与*(a+0)等价, a[1]与*(a+1)等价, 因此a[i]+j就与*(a+i)+j等价, 它表示数组元素a[i][j]的地址。
因此, 二维数组元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j), 它们都与a[i][j]等价, 或者还可写成(*(a+i))[j]。
另外, 要补充说明一下, 如果你编写一个程序输出打印a和*a, 你可发现它们的值是相同的, 这是为什么呢? 我们可这样来理解: 首先, 为了说明问题, 我们把二维数组人为地看成由三个数组元素a[0], a[1], a[2]组成, 将a[0], a[1], a[2]看成是数组名它们又分别是由4个元素组成的一维数组。因此, a表示数组第 0行的地址, 而*a即为a[0], 它是数组名, 当然还是地址, 它就是数组第0 行第0 列元素的地址。
2. 指针的相减运算:(px-py)
如果两个指针px和py所指向的变量类型相同,则可以对它们进行相减运算。px-py运算的结果值是两指针指向的地址位置之间的数据个数。由此看出,两指针相减实质上也是地址计算。它执行的运算不是两指针持有的地址值相减,而是按下列公式得出结果。
((px)-(py)) / 数据长度
上式中(px)和(py)分别表示指针px和py的地址值,所以,两指针相减的结果值不是地址量,而是一个整数。
指针的相减运算一般也用在对数组进行的操作中。
比如:
int x[5],a;
int *px=&x[1],*py=&x[4];
a=py-px;
这里的变量a就表示数组元素x[1]和x[4]之间相隔的元素的个数。
指针加减不是指针值的加减。
指针运算是以指针类型为单位的
指针+常量得到的是一个新指针 具体值为 原指针值+常量*类型大小
指针1-指针2得到的是偏移,具体值为 (指针1值 - 指针2值)/类型大小
对指针进行加1 操作,得到的是下一个元素的地址,而不是原有地址值直接加1。
一个类型为T 的指针的移动,以sizeof(T) 为移动单位。
3. 数组a[5],a和&a的区别
假设有int a[5];
a 实际上是数组a的首元素a[0]的首地址&a[0]。a+1是数组a的下一元素的地址,即&a[0]+sizeof(int)。
&a是数组的首地址,其值和&a[0]相等,但是两者的意义不同。&a+1的含义是下一个数组的首地址。先取数组a的 首地址,该地址的值加上sizeof(a)的值,即&a+5*sizeof(int),得到下一个数组的首地址。
int *p1 = a;//p1指向数组a第一元素的地址,p1+1指向数组a第二元素的地址。
int *p2 = &a;//p2指向数组a的地址,p2+1指向下一个数组的地址。
出题:
inta[5] = {1,2,3,4,5};
int *ptr = (int *)(&a + 1);
cout << *(a + 1) << *(ptr-1);
分析:a 是一个一维数组,数组中有5 个元素。计算&a + 1的步骤: 1.取数组a 的首地址(看成是指针加一,int * p=&a,p+1),该地址的值加上sizeof(a) 的值,即&a + 5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。(int *)(&a+1): 则是把上一步计算出来的地址,强制转换为int * 类型,赋值给ptr(将int (*)[5]类型转换为int *)。a+1 是数组下一元素的首地址,即a[1]的首地址,&a+1 是下一个数组的首地址。所以输出2 。*(ptr-1): 因为ptr 是指向a[5],并且ptr 是int * 类型,所以*(ptr-1) 是指向a[4] ,输出5。