双指针输出说明

double pointer output explanation

你能解释一下输出是如何 -4 的吗?我认为 ++pp; 是 UB 但不确定。您的解释将真正有助于理解。 big-endian 或 little-endian 机器的输出有什么不同吗?

#include <stdio.h>

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

int main(void)
{
    int *p[] = { a, b };
    int **pp = p;
    printf("a=%p, b=%p, p=%p, pp=%p\n", (void*)a, (void*)b, (void*)p, (void*)pp);
    ++pp;
    printf("p=%p, pp=%p *pp=%p\n", (void*)p, (void*)pp, (void*)*pp);
    ++*pp;
    printf("p=%p, pp=%p *pp=%p\n", (void*)p, (void*)pp, (void*)*pp);
    ++**pp;

    printf("%d\n", (++**pp)[a]);
}

我的输出:

a=0x107121040, b=0x107121050, p=0x7ffee8adfad0, pp=0x7ffee8adfad0
p=0x7ffee8adfad0, pp=0x7ffee8adfad8 *pp=0x107121050
p=0x7ffee8adfad0, pp=0x7ffee8adfad8 *pp=0x107121054
-4

Ideone output

记住订阅运算符的定义[],例如this 在线 C 标准草案中定义:

6.5.2.1 Array subscripting

2) ... The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). ...

它说 E1[E2] 等同于 (*((E1)+(E2))。然后很明显 (++**pp)[a]*((++**pp)+(a)) 相同,这又是与*((a)+(++**pp))相同,因此读作a[(++**pp)]。那么++**pp的值为3,而a[3]-4

当您使用数组名称时(在大多数情况下),它会衰减为指向其第一个元素的指针。这意味着 int* p = a;int* p = &a[0]; 完全相同。

所以要了解在这种情况下会发生什么,只需一步一步来。在您第一次 printf 调用时,情况如下所示:

 pp            p           a
+-------+     +------+     +----+----+----+----+
|   +--------->   +--------> -1 | -2 | -3 | -4 |
+-------+     |      |     +----+----+----+----+
              |      |
              +------+     b
              |      |     +----+----+----+----+
              |  +---------> 0  | 1  | 2  | 3  |
              |      |     +----+----+----+----+
              +------+

pp指向p的第一个元素,是指向a的第一个元素的指针。

现在,当您递增 pp 时,它变为指向 p 的第二个元素,它是指向 b 的第一个元素的指针:

 pp            p           a
+-------+     +------+     +----+----+----+----+
|   +   |     |   +--------> -1 | -2 | -3 | -4 |
+---|---+     |      |     +----+----+----+----+
    |         |      |
    |         +------+     b
    |         |      |     +----+----+----+----+
    +--------->  +---------> 0  | 1  | 2  | 3  |
              |      |     +----+----+----+----+
              +------+

然后您递增 *pp。由于 *pp 是指向 b 的第一个元素的指针,因此该指针递增以指向 b 的第二个元素:

 pp            p           a
+-------+     +------+     +----+----+----+----+
|   +   |     |   +--------> -1 | -2 | -3 | -4 |
+---|---+     |      |     +----+----+----+----+
    |         |      |
    |         +------+     b
    |         |      |     +----+----+----+----+
    +--------->      |     | 0  | 1  | 2  | 3  |
              |   +  |     +----+-^--+----+----+
              +---|--+            |
                  +---------------+

然后你递增**pp。此时pp是指向p的第二个元素的指针,所以*pp是指向b的第二个元素的指针。这意味着 **pp 命名了 b 的第二个元素。您将其从 1 增加到 2:

 pp            p           a
+-------+     +------+     +----+----+----+----+
|   +   |     |   +--------> -1 | -2 | -3 | -4 |
+---|---+     |      |     +----+----+----+----+
    |         |      |
    |         +------+     b
    |         |      |     +----+----+----+----+
    +--------->      |     | 0  | 2  | 2  | 3  |
              |   +  |     +----+-^--+----+----+
              +---|--+            |
                  +---------------+

现在,让我们剖析一下(++**pp)[a]++**pp 与之前相同,因此 b 的第二个元素递增为 3.

现在,对于任何指针 ptr 和整数 nptr[n]*(ptr + n) 相同。由于加法是可交换的,因此 ptr + nn + ptr 相同。这意味着 ptr[n]n[ptr].

相同

把这些放在一起,就是说(++**pp)[a]3[a]一样,也就是a[3]一样。 a[3]-4,因此你的结果。

如果将表达式中的所有数组名称表示为它们的衰减值,则最容易理解这一点。 arrayName 作为指针变为 &arrayName[0]。所以在所有的初始化之后,你有:

a[0] = -1, a[1] = -2, a[2] = -3, a[3] = -4
b[0] = 0, b[1] = 1, b[2] = 2, b[3] = 3
p[0] = &a[0], p[1] = &b[0]
pp = &p[0]

递增指针使其指向下一个数组元素,因此在 ++pp 之后我们现在有

pp = &p[1]

++*pp 取消引用 pp,所以它等同于 ++p[1],所以现在我们有

p[1] = &b[1]

++**pp 两次取消引用,所以它等同于 ++b[1],所以现在我们有

b[1] = 2

最后,我们有一个真正令人困惑的表达式 (++**pp)[a]++**pp 再次递增 b[1],所以它的值现在是 3,并且那个值替换了那个表达式,所以它等同于 3[a]。这可能看起来像胡说八道(3 不是数组,你怎么能索引它?),但事实证明在 C 中,x[y] == y[x] 因为索引是根据指针算术定义的.所以3[a]a[3]一样,最后一行打印-4.