双指针输出说明

时间:2019-06-21 22:14:42

标签: c arrays pointers pointer-arithmetic

您能解释一下输出是-4吗?我认为++pp;是UB,但不确定。您的解释将确实有助于理解。大端或小端机器的输出可能有差异吗?

#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

3 个答案:

答案 0 :(得分: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,因此您的结果。

答案 1 :(得分:1)

请记住订阅运算符[]的定义,例如根据{{​​3}}在线C标准草案中的定义:

  

6.5.2.1数组下标

     

2)...下标运算符[]的定义是E1 [E2]为   等同于(*((E1)+(E2))))。 ...

它说E1 [E2]与(*((E1)+(E2)))相同。然后清楚地发现(++**pp)[a]*((++**pp)+(a))相同,再次相同为*((a)+(++**pp)),因此读为a[(++**pp)]。那么++**pp的值为3,而a[3]的值为-4

答案 2 :(得分:1)

如果您将表达式中的所有数组名称表示为其衰减值,则最容易理解这一点。 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