c circular double linked-list:rev traverse为同一节点提供不同的列表指针地址

时间:2014-03-28 02:30:18

标签: c pointers doubly-linked-list

相关帖子1:c circular double linked-list delete_node - iterate traverses deleted node on first pass after delete

相关帖子2:c circular double linked-list: traverses fwd/rev for end node gives different pointer address

在使用循环双链表时,我在stackoverflow的帮助下创建了一个delete_node函数,该函数使用列表上的正向或反向迭代来到达要删除的节点。该函数将链接列表的地址作为参数来实现删除(而不是对列表的指针引用)。正向和反向迭代的混合只是为了提高效率,以防止在正向迭代或反向迭代开始时遍历整个列表到达接近结尾的节点。

full source:
http://www.3111skyline.com/dl/dev/prg/src/testlld.c.txt
compile with:  gcc -Wall -o tlld testlld.com

void
delete_node (rec **list, int node)
{

// test that list exists
if (!*list) {
    fprintf (stdout,"%s(), The list is empty\n",__func__);
    return;
}

// get size of list
int szlist = getszlist (*list);

// test node < szlist
if (node >= szlist || node < 0) {
    fprintf (stderr, "%s(), Error: record to delete is out of range (%d)\n", __func__, node);
    return;
}

// find the node'th node with balanced search
// search fwd for 0->szlist/2, rev end->szlist/2
if (node != 0  && node >= szlist/2) {
    while (szlist - node++)
    list = &(*list)->prev;

    list = &(*list)->prev;  // hack to get correct list ptr before delete
    list = &(*list)->next;  // when traversing list in reverse
} else
    while (node--)
    list = &(*list)->next;

// create pointer to node to delete
rec *victim = *list;

// non-self-reference node means just rewire
if (victim != victim->next) {
    (victim->prev)->next = victim->next;
    (victim->next)->prev = victim->prev;
    *list = victim->next;
} else {      // deleted node was self-referenced. last node
    *list = NULL;
}

free (victim);  // delete the node
}

但是,我遇到了一个反向迭代的问题,其中到达所需节点时报告的指针地址与通过前向迭代到达时为同一节点报告的地址不同。在related post 2中,这是通过前向迭代解释的,将节点指向节点指针(节点-1)和反向迭代指向(节点+ 1),同时两个节点指针正确指向(节点)。我不相信是这样的。

通过在与所需节点反向迭代之后转储指针,然后再迭代一个额外的步骤(node-&gt; prev)再次转储指针,然后在向前方向上退回到所需节点,可以很容易地看到问题( node-&gt; next)并再次转储指针。报告的节点地址发生了变化。以下是节点30的示例:

list pointer state after reverse iteration to node: 30
29 - prev: 0x605070  cur: 0x605100  next: 0x605078
30 - prev: 0x605100  cur: 0x605190  next: 0x605108
31 - prev: 0x605190  cur: 0x605108  next: 0x605198

list pointer state after next step to node: 29
28 - prev: 0x604fe0  cur: 0x605070  next: 0x604fe8
29 - prev: 0x605070  cur: 0x605100  next: 0x605078
30 - prev: 0x605100  cur: 0x605078  next: 0x605108

list pointer state after forward step to node: 30
29 - prev: 0x605070  cur: 0x605100  next: 0x605078
30 - prev: 0x605100  cur: 0x605078  next: 0x605108
31 - prev: 0x605190  cur: 0x605108  next: 0x605198

我不明白!在上面3的第一个块中,节点30的列表指针在反向迭代时报告其地址为cur: 0x605190。这看起来很糟糕,就像代码有问题一样,但是没有。然而,有些事情是错误的,因为node 30 cur: != node 29 next:。反向list = &(*list)->prev;中再继续一步,给出下一个地址块,列表指针地址已更改为cur: 0x605078。咦?但是,您可以通过查看现在节点29和...的地址,在反向迭代中看到问题继续存在。 28.节点30存在问题的同一问题现在存在于节点29处(通过附加步骤 - > prev的优点)。

现在使用list = &(*list)->next;移回节点30,可以使用地址0x605078删除节点30,一切正常。但我无法理解为什么我不能反向迭代到节点30并使删除有效?为什么反向交互为节点30 0x605190提供的地址不同于从前向0x605078到达的地址。

最后,在&gt; prev, - &gt;后续步骤之后,您可以在node 30 cur: 0x605078node 31 prev: 0x605190之间看到相同类型的问题。咦? (当达到反向迭代时,这是最初为节点30报告的地址??这里发生了什么?

完整source code可用。只需使用gcc -Wall -o tlld testlld.com进行编译即可。该代码创建了一个50节点链接列表来进行操作。您可以强制列表将所有节点删除为./tlld {49..0}的测试。

1 个答案:

答案 0 :(得分:0)

正如我在上一个帖子中所说,问题在于你正在查看指针变量的地址,而不是指针变量的值(即变量指向的位置)。 您也不想更新* list,除非删除的条目是第一个。您的调用函数依赖于textfile指向列表的开头。

这是基于我在最后一个线程上提供的代码的delete_node_debug的更新版本。我在循环中使用了victim而不是*list,这使得*list指向列表的开头。然后,最后,您可以检查是否要删除列表的开头victim == *list

void
  delete_node_debug (rec **list, int node)
{

  // test that list exists
  if (!*list) {
    fprintf (stdout,"%s(), The list is empty\n",__func__);
    return;
  }

  // get size of list
  int szlist = getszlist (*list);

  // test node < szlist
  if (node >= szlist || node < 0) {
    fprintf (stderr, "%s(), Error: record to delete is out of range (%d)\n", __func__,     node);
    return;
  }

  rec *victim = *list;
  // find the node'th node with balanced search
  // search fwd for 0->szlist/2, rev end->szlist/2
  if (node != 0  && node >= szlist/2) {
    while (szlist - node++)
      victim = victim->prev;
    fprintf (stderr, "\nlist pointer state after reverse iteration to node: %d\n",
      victim->lineno);
    psurround (&victim);
  } else
    while (node--)
      victim = victim->next;

  // non-self-reference node means just rewire
  if (victim != victim->next) {
    (victim->prev)->next = victim->next;
    (victim->next)->prev = victim->prev;
    // If we are deleting the first item, then we need to change the passed in pointer
    if (victim == *list)
      *list = victim->next;
  } else {      // deleted node was self-referenced. last node
    *list = NULL;
  }

  free (victim);  // delete the node
}

另外,更改调试输出函数psurround以打印出指针值,而不是指针变量的地址:

void
psurround (rec **list) {

    fprintf (stderr, "%2d - prev: %p  cur: %p  next: %p\n",
        (*list)->prev->lineno,
        (*list)->prev->prev,
        (*list)->prev,
        (*list)->prev->next);
    fprintf (stderr, "%2d - prev: %p  cur: %p  next: %p\n",
        (*list)->lineno, (*list)->prev, *list, (*list)->next);
    fprintf (stderr, "%2d - prev: %p  cur: %p  next: %p\n\n",
        (*list)->next->lineno,
        (*list)->next->prev,
        (*list)->next,
        (*list)->next->next);
}

一般来说,我认为只要你在同一个词中有多个&amp; s和* s(比如*&amp;(* list)),你需要重新考虑发生的事情。