【问题标题】:How does pointer dereferencing work?指针解引用如何工作?
【发布时间】:2011-03-21 09:19:38
【问题描述】:
#define SWAP_PTRS(a, b) do { void *t = (a); (a) = (b); (b) = t; } while (0)

Node* MergeLists(Node* list1, Node* list2) 
{
  Node *list = NULL, **pnext = &list;

  if (list2 == NULL)
    return list1;

  while (list1 != NULL)
  {
    if (list1->data > list2->data)
      SWAP_PTRS(list1, list2);

    *pnext = list1;
    pnext = &list1->next;
    list1 = *pnext;
  }

  *pnext = list2;
  return list;
}

此代码来自这里,this question 的选择答案。

这里的 3 行我看不懂:

*pnext = list1;
pnext = &list1->next;
list1 = *pnext;

谁能帮帮我?给我解释一下?

已编辑:我可以更改这两行吗:

pnext = &list1->next;
list1 = *pnext;

 list = list-> next; 

【问题讨论】:

  • 这不是 C++; SWAP_PTRS 宏在 C++ 中不起作用。 (它依赖于 void* 到 C++ 中不存在的任何指针类型的隐式转换。)
  • 谢谢提醒。我已将标签更改为 C

标签: c pointers dereference


【解决方案1】:

从一开始:您将返回两个列表和一个新的列表头。 pnext 最初指向那个。

代码旨在尽可能少地重新分配指针,因此尽量保持输入列表的初始“下一个”指针不变。

list1          pnext->list->Null
|
V
o->o->o...

list2
|
V
o->o->o...

Swap 是为了确保较小的元素是 list1 的第一个元素。这些行的作用是:

一步一步来:

*pnext = list1;

获取*pnext(第一次迭代之前的列表)指向包含最小元素的节点:

list1
|
V
o->o->o...
^
|
list<-pnext

list2
|
V
o->o->o...

.

pnext = &list1->next;

是棘手的部分,如前所述, & 运算符的优先级较低。它也很难以图形方式显示,因为它实际上是查看 Node 构造的一部分。不过是这样的:

list1
|
V
o---->o->o...
^     ^
|     |
list  x<-pnext

list2
|
V
o->o->o...

其中x是list1指向的o的下一个指针。

list1 = *pnext;

在处理 list1 的第一个元素时将其前移。

   list1<-pnext
   |
   V
o->o->o->...
^
|
list

list2
|
V
o->o->o->...

从这里开始,您与列表无关,因为您想将其作为合并列表的头部返回。

不变量 pnext 指向最后处理元素的 next 指向的位置,也就是 list1-2 中最小的元素应该去的位置。有趣的事情发生在交换中,尝试自己制定确切的程序(很难像这样画,并且很好的练习来理解 ** 做了什么)。如果我找到一个好的方法来绘制它,我可能会添加它。

你不能使用list = list-&gt; next;,因为它会做这样的事情:

   list1
   |
   V
o->o->o->...
   ^
   |
   list

list2
|
V
o->o->o->...

这意味着你会失去那个孤独的 o(以及随着循环的进行而最终失去的一切)。

编辑:最后的*pnext = list2; 这样做:

循环终止(上述语句之前的状态):

         list1<-pnext
         |
         V
o->o->o->null
^
|
list

      o->o->o->...
      ^
      |
      list2

声明后:

         list1
         |
         V              
o->o->o  Null
^     |
|     |
list  |
      V
      o->o->o->...
      ^
      |
      list2<-pnext

该语句将剩余的列表附加到列表的末尾。然后返回 Node* 列表,指向合并列表的头部。

编辑2:

一直以来,pnext 最好这样表示:

         list1
         |
         V              
o->o->o  Null
^     |
|     |<-pnext
list  |
      V
      o->o->o->...
      ^
      |
      list2

这意味着它指向最后处理的节点的下一个指针。

【讨论】:

  • 比你@mcyalcin 好多了!这是一个很好的解释。非常感谢!我明白了
  • 嗨@mcyalcin,还有一个问题,“*pnext = list2;”呢?这一行是否修改了“列表”指针?现在有点困惑。谢谢!
  • @Josh 你的意思是 pnext=list2;最后我猜。那是在循环终止之后,这意味着其中一个列表为空,因此剩余的列表将附加到列表的末尾。该分配与 Node 列表无关(您可能离它很远)。我会尝试在编辑中绘制它。
  • @Josh 现在我想起来了,为了清楚起见,pnext 实际上应该指向箭头(这就是我说它实际上指向节点构造的一部分时的意思。想想它指向箭头。嗯,另一个小编辑。
  • 谢谢麦克亚克林。我想我终于理解了这段代码。 :)
【解决方案2】:

pnext 是一个指向Node的指针

*pnext”表示您正在使用pnext 指向的值。因此:

*pnext = list1;

意味着pnext 指向的任何指针现在都指向与list1 相同的东西。


为了清楚起见,下一行应该用括号括起来:

pnext = &(list1->next);

list1-&gt;next 表示您正在访问list1 所指向的next 属性,&amp; 表示获取该元素的地址。本质上意味着您正在使 pnext 指向指向下一个元素的指针。


最后一行:

list1 = *pnext;

表示你将pnext指向的值赋值给list1

【讨论】:

  • 我可以更改这两行吗:pnext = &list1->next; list1 = *pnext; to list = list-> next;
【解决方案3】:

list1Node* 类型,pnextNode** 类型,这意味着它应该保存类型为 Node* 的变量的地址。

*pnext = list1;

当您取消引用 pnext 时,您会得到 Node*。所以,这个分配是正确的。

pnext = &list1->next;

这里-&gt; 的优先级高于&amp;。因此,它返回一个指针的地址(即 Node* 类型的地址)

list1 = *pnext;

这与第一个语句正好相反。取消引用pnext 会得到Node*,它可以分配给list1


是的,你可以改变这个(即,逻辑上他们做同样的事情)-

pnext = &list1->next;
list1 = *pnext;

list1 = list1->next ;

但是,你有一个声明-

 *pnext = list2;

如果 pnext 没有像两步序列中那样初始化,取消引用未初始化的指针(即 *pnext )将导致分段错误。这就是原因。

【讨论】:

  • 我可以更改这两行吗:pnext = &list1->next; list1 = *pnext; to list = list-> next;?
  • 那为什么那个帖子里的编码员给出了如此神秘的代码???你可以检查那个帖子,其他解决方案也遵循这种风格......我不确定我对这个改变是否正确。
  • @Josh 不,你不能!您将丢失指向要返回的列表头部的指针,这是代码的全部内容。那里的代码实际上非常优雅。我会写一个解释(我认为这需要一些数字才能清楚)。
【解决方案4】:

尝试安装visual studio express版。 在 VS-2012 中调试程序后,只需将鼠标悬停在指针上,它就会通过取消引用地址向您显示内容。

【讨论】:

    猜你喜欢
    • 2015-01-20
    • 1970-01-01
    • 2022-01-11
    • 2018-02-04
    • 1970-01-01
    • 2010-10-18
    • 2023-04-10
    • 1970-01-01
    相关资源
    最近更新 更多