为什么 *pp[0] 等于 **pp?

3

我正在努力理解指针,阅读了一些关于指向指针的帖子,但仍然无法弄清楚为什么这个程序可以正常运行。

#include <stdio.h>
#include <assert.h>

int main(){
    char* p = "abc";

    char** pp[2];
    pp[0] = &p;
    assert(*pp[0]==**pp);

    printf("Comparison: %s, %s\n",**pp, *pp[0]);

    return 0;
}

据我现在的理解,内存大致如下所示。
Memory Address (hex)    Variable name    Contents
1000                                     'a' == 97 (ASCII)
1001                                     'b' == 98
1002                                     'c' == 99
1003                                     0
...
2000-2003               p                1000 hex
...
3000-3003               pp[0]            2000 hex

假设我对内存的理解是正确的...我期望*pp[0]会进入内存并显示出...

因此,pp[0]指向p的地址,即0x2000,并通过取消引用来获取地址0x2000的内容,在我的看法中,这意味着我将获得0x1000,但实际情况并非如此,因为程序的输出是:

输出

abc, abc

在pp的情况下,我认为它首先会取消引用pp,这将给我们pp指向的任何内容,也就是0x2000的内容(即0x1000),然后再次取消引用,我们得到地址0x1000的内容。

为什么它们相等?我错过了什么地方吗?


1
因为 pp 可以缩写为 &pp[0],而且 **&pp[0] 显然与 *pp[0] 相同(因为无论何时 *&x 都等同于 x)。 - user253751
3个回答

5
在大多数情况下,如果你有一个数组a,那么a代表的是&a[0]。(有一些例外情况,比如sizeof a或者&a)。
因此,**pp实际上意味着**&pp[0]&pp[0]pp[0]的地址,所以*&pp[0]等同于pp[0],因此**&pp[0]等同于*pp[0]

这个答案非常有道理,也许这就是我将来看待它的方式,但我的推理更多地遵循@Peter的说法,他提到*pp[0]会有效地解引用两次,否则根据我所写的,它就没有意义。我希望这两个答案不会相互冲突,感谢您的帮助。 - Omar Muñoz
如果pp是一个数组,那么pp[0]实际上并不会在底层解除引用。 (好吧,它不会比访问任何其他变量更多地解除引用)@OmarMuñoz - user253751

2

pp[0]始终与*pp相同,无论您处理什么类型的指针。

由于方括号的高操作符优先级,*pp[0]与*(pp[0])相同......根据上面的句子,这与*(*pp)相同,这与**pp相同。证毕。


2
所以pp [0]指向p的地址,即0x2000,通过解引用我希望获得地址0x2000的内容。这就是你的思维误区所在,但可以理解。在C中,赋值的右侧或通常是左值(俗称:变量)的评估更准确地说是左值到右值的转换,已经是解引用!例如,int i,j = 0; i = j; 实际上对j进行了解引用;j是一个地址常量,并且赋值涉及存储在那里的值,j的值,因此赋值相当于i = 0。其他语言(如Algol68)更精确:一个人实际上会写int i; int * pi = i;,这完全有意义(pi现在指向i)。这就是为什么当您 明确 解引用pp [0]时,使用* pp [0],您实际上正在解引用它两次:首先,您查看地址0x2000的内容,即0x1000,然后您会解引用它以读取0x1000处的内存。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接