指针表达式:** ptr ++,* ++ * ptr和++ ** ptr使用

时间:2013-07-19 17:55:42

标签: c arrays pointers

我正在尝试使用C指针文献。在其中一个插图中,我遇到了以下代码。

# include <stdio.h>

int main()
{
     static int a[]={0,1,2,3,4};
     static int *p[]={a, a+1, a+2, a+3, a+4};

     int **ptr;

     ptr =p;

     **ptr++;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     *++*ptr; 
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     ++**ptr;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     return 0;
}

我收到输出为。

1 1 1
1 2 2
1 2 3

我在为这个输出辩护时遇到了问题。我在副本上制作了很多盒子,以便轻松掌握问题。我能够证明输出1 1 1是合理的,我的问题始于语句*++*ptr

因为,一元操作符从右到左执行。因此,首先处理*ptr,然后ptr处的值将增加。 在这个增量之后,我不确定会发生什么,书中说不知怎的p也会增加到指向此数组中的下一个元素。输出1 2 2只能通过增加p来实现。

我不确定这种问题是否完全符合stackoverflow 我尽我所能,浪费了至少10页,上面画着盒子。

任何澄清都将不胜感激。

4 个答案:

答案 0 :(得分:53)

请记住,在大多数表达式中,数组名称可以很容易地衰减成指向第一个元素的指针(阅读@ exceptions where array name not decaying into a pointer to first element?可以回答的H2CO3)。 为了更好地理解,请考虑我的图表:

首先,假设a存储在内存中,如下所示。

  a 
+----+----+----+----+---+
|  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲
  |    |    |    |    | 
  a    a+1  a+2  a+3  a+3

声明static int *p[] = {a, a+1, a+2, a+3, a+4};创建一个指向整数的新指针数组,其值如下:

p[0] == a
p[1] == a + 1
p[2] == a + 2
p[3] == a + 3
p[4] == a + 4

现在,p也可以假设存储在内存中,如下所示:

  p
+----+----+----+----+-----+
| a  |a +1| a+2| a+3| a+4 | 
+----+----+----+----+-----+
  ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |
  p    p+1  p+2  p+3  p+4

分配ptr = p;后,事情会是这样的:

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  |a +1| a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
  ptr 


Notice: ptr points to first location in pointer array p[]

表达式:** ptr ++;

现在我们在第一个printf语句之前考虑表达式**ptr++;

  1. ptr等于p,它是指针数组中第一个元素的地址。  因此,ptr指向数组中的第一个元素p[0](或者我们可以说ptr == &p[0])。

  2. *ptr表示p[0]  由于p[0]a,因此*ptra(因此*ptr == a)。

  3. 由于*ptra,因此**ptr*a == *(a + 0) == a[0]0 1}}。

  4. 请注意,在表达式**ptr++;中,我们不会将其值赋给任何lhs变量  因此**ptr++;的效果与ptr++; == ptr = ptr + 1 = p + 1相同  这样,表达式ptr指向p[1]之后(或者我们可以说ptr == &p[1])。

  5. 打印-1:

    在第一次印刷之前:

      p                              a 
    +----+----+----+----+-----+    +----+----+----+----+---+
    | a  | a+1| a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
    +----+----+----+----+-----+    +----+----+----+----+---+
      ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
      |    |    |    |    |          |    |    |    |    | 
      p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
           ptr 
    
    
    Notice: ptr is equals to  p + 1 that means it points to p[1]
    

    现在我们可以理解第一 printf:

    1. ptr - p输出1因为:
      ptr = p + 1ptr - p == p + 1 - p == 1

    2. *ptr - a输出1因为:
      ptr = p + 1*ptr == *(p + 1) == p[1] == a + 1
       这意味着:*ptr - a = a + 1 - a == 1

    3. **ptr输出1因为:
       来自第2点的*ptr == a + 1  所以**ptr == *(a + 1) == a[1] == 1

    4. 表达式:* ++ * ptr;

      在第一次printf之后,我们有一个表达式*++*ptr;

      正如我们从第2点所知,*ptr == p[1]。 因此,++*ptr(即++p[1])会将p[1]增加到a + 2

      再次理解,在表达式*++*ptr;中,我们不会将其值分配给任何lhs变量,因此*++*ptr;的效果只是++*ptr;

      现在,在第二次打印之前,事情变成了:

        p                              a 
      +----+----+----+----+-----+    +----+----+----+----+---+
      | a  |a+2 | a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
      +----+----+----+----+-----+    +----+----+----+----+---+
        ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
        |    |    |    |    |          |    |    |    |    | 
        p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
             ptr 
      
      
      Notice: p[1] became a + 2 
      

      打印-2:

      现在我们可以理解第二 printf:

      1. ptr - p输出1因为:
        ptr = p + 1ptr - p == p + 1 - p == 1

      2. *ptr - a输出2因为:
        ptr = p + 1所以*ptr == *(p + 1) == p[1] == a + 2
        这意味着:*ptr - a == a + 2 - a == 2

      3. **ptr输出2因为:
         来自第2点的*ptr == a + 2   所以**ptr == *(a + 2) == a[2] == 2

      4. 表达式:++ ** ptr;

        现在在第三个printf之前表达++**ptr;

        正如我们从第3点所知,**ptr == a[2]。 因此++**ptr == ++a[2]会将a[2]增加到3

        所以在第三次打印之前:<​​/ p>

          p                              a 
        +----+----+----+----+-----+    +----+----+----+----+---+
        | a  | a+2| a+2| a+3| a+4 |    |  0 |  1 | 3  | 3  | 4 |
        +----+----+----+----+-----+    +----+----+----+----+---+
          ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
          |    |    |    |    |          |    |    |    |    | 
          p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
               ptr 
        
        
         Notice: a[2] = 3
        

        打印-3:

        现在我们可以理解第三 printf:

        1. ptr - p输出1因为:
          ptr = p + 1所以ptr - p == p + 1 - p == 1

        2. *ptr - a输出2因为:
          ptr = p + 1所以*ptr == *(p + 1) == p[1] == a + 2
           这意味着:*ptr - a = a + 2 - a == 2

        3. **ptr输出3因为:
          来自第2点的*ptr == a + 2  所以**ptr == *(a + 2) == a[2] == 3

        4. 修改注意:两个指针的差异类型为ptrdiff_t,为此,正确的转化说明符为%td,而不是%d

          还有一点:
          我希望补充一下,因为我相信它对新学员有用

          假设我们在return 0;

          之前的代码中有两行以及另外一行4 th printf
          **++ptr;    // additional 
          printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);  // fourth printf
          

          可以检查此工作代码@ Codepade,此行输出2 2 3

          表达式:** ++ ptr;

          由于ptr等于p + 1,因此增量++操作ptr变为p + 2(或者我们可以说ptr == { {1}})。
          之后,双重参与操作&p[2] ==&gt; ** == **(p + 2) == *p[2] == *(a + 2) == { {1}}。
          现在,再次因为我们在此语句中没有任何赋值操作,因此表达式a[2]的效果仅为3

          表达式**++ptr;之后的事情如下图所示:

          ++ptr;

          打印-4:

          考虑 Forth printf我添加了问题:

          1. **++ptr;输出 p a +----+----+----+----+-----+ +----+----+----+----+---+ | a | a+2| a+2| a+3| a+4 | | 0 | 1 | 3 | 3 | 4 | +----+----+----+----+-----+ +----+----+----+----+---+ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ | | | | | | | | | | p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3 ptr Notice: ptr is equals to p + 2 that means it points to p[2] 因为:
            ptr - p所以2 == ptr = p + 2 == ptr - p

          2. p + 2 - p输出2因为:
            *ptr - a所以2 == ptr = p + 2 == *ptr == *(p + 2)
             这意味着:p[2] = a + 2 == *ptr - a

          3. a + 2 - a输出2因为:
            点{2}以上**ptr == 3  所以*ptr == a + 2 == **ptr == *(a + 2)

答案 1 :(得分:8)

如果你编译时有一些警告(clang甚至不需要任何标志),你会发现你的程序有三个无关的*运算符。简化你疯狂的表达产生:

ptr++;
++*ptr;
++**ptr;

从那以后,你应该能够清楚地看到发生了什么:

  1. ptr++只增加ptr,因此它指向p的第二个元素。完成此操作后,ptr - p始终为1

  2. ++*ptr增加ptr指向的任何内容。这会将p的第二个元素更改为指向a的第三个元素而不是第二个元素(它被初始化为)。这使*ptr - a等于2。同样,**ptr是来自2的{​​{1}}。

  3. a增加++**ptr所指向的任何内容。这会增加ptr的第三个元素,使其成为a

答案 2 :(得分:2)

请记住,++的优先级高于*,所以当你执行**ptr++时,会增加指针并对旧值进行双重解除引用,这只会导致崩溃。它不是一个有效的指针指针,你的编译器(至少启用了警告)应警告你一个未使用的结果。

 static int a[]={0,1,2,3,4};

 static int *p[]={a, a+1, a+2, a+3, a+4};

 int **ptr;

 ptr = p; // ptr = &(p[0]); *ptr = a; **ptr = 0.

 **ptr++; // ptr = &(p[1]); *ptr = a+1; **ptr = 1
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 *++*ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 2; p = {a, a+2, a+2, a+3, a+4} 
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 ++**ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 3; a = {0, 1, 3, 3, 4}
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

答案 3 :(得分:2)

地址int*的{​​{1}}值已增加语句ptr(实际上是*++*ptr;部分,前导++*ptr是未使用的解除引用)。因此,*的扩展应该如下所示:

int *p[]

最终的int *p[]={a, a+2, a+2, a+3, a+4};现在增加了地址++**ptr;的值,因此原始数组现在看起来像这样:

a+2