我正在尝试使用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页,上面画着盒子。
任何澄清都将不胜感激。
答案 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[]
现在我们在第一个printf语句之前考虑表达式**ptr++;
。
ptr
等于p
,它是指针数组中第一个元素的地址。
因此,ptr
指向数组中的第一个元素p[0]
(或者我们可以说ptr
== &p[0]
)。
*ptr
表示p[0]
由于p[0]
为a
,因此*ptr
为a
(因此*ptr
== a
)。
由于*ptr
为a
,因此**ptr
为*a
== *(a + 0)
== a[0]
即0
1}}。
请注意,在表达式**ptr++;
中,我们不会将其值赋给任何lhs变量
因此**ptr++;
的效果与ptr++;
== ptr = ptr + 1
= p + 1
相同
这样,表达式ptr
指向p[1]
之后(或者我们可以说ptr
== &p[1]
)。
打印-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:
ptr - p
输出1
因为:
ptr = p + 1
,ptr - p
== p + 1 - p
== 1
*ptr - a
输出1
因为:
ptr = p + 1
,*ptr
== *(p + 1)
== p[1]
== a + 1
这意味着:*ptr - a
= a + 1 - a
== 1
**ptr
输出1
因为:
来自第2点的*ptr
== a + 1
所以**ptr
== *(a + 1)
== a[1]
== 1
在第一次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:
ptr - p
输出1
因为:
ptr = p + 1
,ptr - p
== p + 1 - p
== 1
*ptr - a
输出2
因为:
ptr = p + 1
所以*ptr
== *(p + 1)
== p[1]
== a + 2
这意味着:*ptr - a
== a + 2 - a
== 2
**ptr
输出2
因为:
来自第2点的*ptr
== a + 2
所以**ptr
== *(a + 2)
== a[2]
== 2
现在在第三个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:
ptr - p
输出1
因为:
ptr = p + 1
所以ptr - p
== p + 1 - p
== 1
*ptr - a
输出2
因为:
ptr = p + 1
所以*ptr
== *(p + 1)
== p[1]
== a + 2
这意味着:*ptr - a
= a + 2 - a
== 2
**ptr
输出3
因为:
来自第2点的*ptr
== a + 2
所以**ptr
== *(a + 2)
== a[2]
== 3
修改注意:两个指针的差异类型为ptrdiff_t
,为此,正确的转化说明符为%td
,而不是%d
。
还有一点:
我希望补充一下,因为我相信它对新学员有用
假设我们在return 0;
**++ptr; // additional
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr); // fourth printf
可以检查此工作代码@ Codepade,此行输出2 2 3
。
由于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我添加了问题:
**++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
p + 2 - p
输出2
因为:
*ptr - a
所以2
== ptr = p + 2
== *ptr
== *(p + 2)
这意味着:p[2]
= a + 2
== *ptr - a
a + 2 - a
输出2
因为:
点{2}以上**ptr
== 3
所以*ptr
== a + 2
== **ptr
== *(a + 2)
答案 1 :(得分:8)
如果你编译时有一些警告(clang甚至不需要任何标志),你会发现你的程序有三个无关的*
运算符。简化你疯狂的表达产生:
ptr++;
++*ptr;
++**ptr;
从那以后,你应该能够清楚地看到发生了什么:
ptr++
只增加ptr
,因此它指向p
的第二个元素。完成此操作后,ptr - p
始终为1
。
++*ptr
增加ptr
指向的任何内容。这会将p
的第二个元素更改为指向a
的第三个元素而不是第二个元素(它被初始化为)。这使*ptr - a
等于2
。同样,**ptr
是来自2
的{{1}}。
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