您能解释一下输出是-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
答案 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
和整数n
,ptr[n]
与*(ptr + n)
相同。由于加法是可交换的,因此ptr + n
与n + 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
。