指针加减操作,当a为数组时a和&a的区别

来源:互联网 发布:31岁转行做数据分析师 编辑:程序博客网 时间:2024/04/29 23:19

求值: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。