31

我正在尝试 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增加 at 的值。在这个增量之后,我不确定会发生什么,这本书说不知何故p也会增加以指向该数组中的下一个元素。输出1 2 2只能通过增量来实现p

我不确定这种问题是否完全适合stackoverflow。
我尽了最大的努力,浪费了至少 10 页,上面画了框。

任何澄清将不胜感激。

4

4 回答 4

54

请记住,在大多数表达式中,数组名称很容易衰减为指向第一个元素的指针(阅读一些异常,其中数组名称没有衰减为指向第一个元素的指针? @ H 2 CO 3巧妙地回答了)。
为了更好地理解,请考虑我的图表:

首先,假设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++;

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

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

  2. *ptr意味着p[0] 并且因为p[0]a,所以 *ptra(所以*ptr== a)。

  3. 并且因为*ptris a, then **ptris *a== *(a + 0)== a[0]那就是0.

  4. 请注意,在表达式中**ptr++;,我们不会将其值分配给任何 lhs 变量。
    所以 of 的效果就和== =**ptr++;一样, 就是在这个表达式指向之后(或者我们可以说== )。ptr++;ptr = ptr + 1p + 1
    ptrp[1]ptr&p[1]

打印一:

在第一次 printf 事情变成:

  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]

现在我们可以理解First printf:

  1. ptr - p输出1因为:
    ptr = p + 1, 所以ptr - 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因为:
    *ptr==a + 1从第 2 点开始
    所以**ptr== *(a + 1) == a[1]==1

表达式:**++*ptr;

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

正如我们从上面第 2 点知道的那样*ptr== p[1]。因此,++*ptr(即++p[1])将p[1]增加到a + 2

再次理解,在表达式中*++*ptr;我们没有将它的值赋给任何 lhs 变量,所以效果 *++*ptr;就是++*ptr;.

现在,在第二个 printf 事情变成:

  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 + 1, 所以ptr - p== p + 1 - p==1

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

  3. **ptr输出2因为:
    *ptr==a + 2从第 2 点开始
    所以**ptr== *(a + 2) == a[2]==2

表达式:++**ptr;

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

正如我们从上面第 3 点知道的那样**ptr== a[2]。所以++**ptr==++a[2]a[2]增加到3

所以在第三个 printf 事情变成:

  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 + 1so *ptr == *(p + 1)== p[1]==a + 2
    这意味着:*ptr - a= a + 2 - a==2

  3. **ptr输出3因为:
    *ptr==a + 2从第 2 点开始
    所以**ptr== *(a + 2) == a[2]==3

编辑注意:两个指针的区别是 type ptrdiff_t,为此,正确的转换说明符是%td, not %d

补充一点:
我想补充一下,因为我相信这对新学习者会有帮助

假设我们之前在您的代码中有以下两行,其中还有一个 4 th printfreturn 0;

**++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== &p[2])。
在双重尊重操作之后** ==> **(p + 2) == *p[2]== *(a + 2)== a[2]== 3
现在,再次因为我们在这个语句中没有任何赋值操作,所以表达式**++ptr;的效果只是++ptr;.

所以表达式后的东西**++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] 

打印 4:

考虑到我在问题中添加的Forth printf:

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

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

  3. **ptr输出3因为:
    *ptr==a + 2从上面第 2 点开始
    所以**ptr== *(a + 2) == a[2]==3

于 2013-07-19T18:04:13.603 回答
8

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

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

从那里,您应该能够非常清楚地看到正在发生的事情:

  1. ptr++只是递增ptr,所以它指向 的第二个元素p。此操作ptr - p后将永远1

  2. ++*ptr递增ptr. 这会将第二个元素更改p为指向第三个元素a而不是第二个元素(它被初始化为)。这使得*ptr - a等于2。同样**ptr2from a

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

于 2013-07-19T18:01:57.570 回答
2

int*address 处的值ptr已由语句递增*++*ptr;(实际上是按++*ptr部分递增,前导*是未使用的取消引用)。因此, 的扩展int *p[]现在应该如下所示:

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

final++**ptr;现在增加了 address 的值a+2,所以原始数组现在看起来像这样:

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

于 2013-07-19T18:13:45.057 回答
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);
于 2013-07-19T18:06:38.990 回答