【问题标题】:Branchless linked list remove entry in C无分支链表删除 C 中的条目
【发布时间】:2021-02-18 01:27:08
【问题描述】:

this TED talk 中,Torvalds 提出了一个不带 if 条件的删除入口函数。我尝试在下面的代码中进行模拟,但如果要删除的条目是列表的头部,则它不起作用。 1.)为什么这不能专门去除头部? 2.) 由于我们从不释放条目,这种方法不会产生内存泄漏吗?

/** a data structure representing a node **/
struct node {
        int data;
        struct node* next;
};

/** create a new node and return a pointer to it**/
struct node* new_Node(int data)
{
        struct node* newP = malloc(sizeof(struct node));
        newP-> data = data;
        newP-> next = NULL;
        return newP;
}
/** function to print out a list**/
void list_print(struct node* head)
{
        printf("Begin List Print:\n");
        struct node* tmp = malloc(sizeof(struct node));
        tmp = head;

        while(tmp != NULL ) {
                printf("%d\n", tmp->data);
                tmp = tmp->next;
        }
        printf("End List Print\n\n");

}
 /** function to delete one node **/
void list_remove_entry(struct node* head, struct node* entry)
{
        struct node** indirect = &head;
    
        while((*indirect) != entry) {
                indirect = &(*indirect)->next;
        }
        *indirect = (*indirect)->next;
}
    
/** the program entry point**/
int main()
{
        struct node* head = new_Node(1);
        struct node* n1 = new_Node(2);
        struct node* n2 = new_Node(3);
        head->next = n1;
        n1->next = n2;
    
        list_print(head);
    
        list_remove_entry(head, head);
        list_print(head);
    
        return 0;
}

【问题讨论】:

  • Torvalds 方法依赖于使用node** 参数。
  • 是的,如果您从不释放该条目,您就会发生内存泄漏。他可能会假设一种垃圾收集语言。

标签: c pointers singly-linked-list


【解决方案1】:

TED演讲中的代码没有将head作为参数,它是一个全局变量。

结果,当它发生时

*indirect = entry->next;

如果要删除的条目等于head,它将修改head 变量,因为while 循环立即停止并且indirect 仍然包含&head

当您将head 设为参数时,这并不相同,因为现在您只是在修改局部变量,而不是调用者的变量。请参阅Changing address contained by pointer using function,了解如何重新设计函数来解决这个问题(这也在@tadman 的回答中)。

在回答您的第二个问题时,是的,这会造成内存泄漏。 Linus 的示例只是为了说明编写此函数的两种方法的一个特定方面,因此他省略了与该差异无关的所有内容。您可以通过将作业替换为:

(*indirect)->next = (*indirect)->next->next;
free(*indirect);

请注意,他还忽略了错误检查。他的代码假定会找到该条目,因此他在取消引用之前从不检查*indirect == NULL(并且我上面的分配不检查双重间接)。

这不是编程课,只是一个简单的示例,需要放在幻灯片上。

【讨论】:

  • 传递 struct node** head 确实是解决方案。然而,free(*indirect); *indirect 之前 = (*indirect)->next;似乎删除了整个列表。
【解决方案2】:

你在这里做了很多巫毒编程:

void list_remove_entry(struct node* head, struct node* entry)
{
  // Takes the address of a local argument
  struct node** indirect = &head;
    
  while((*indirect) != entry) {
    indirect = &(*indirect)->next;
  }

  // Updates local variable, no impact on caller's version of same
  *indirect = (*indirect)->next;
}

如果您一开始没有获得间接变量,那么在函数内部将其转换为一个为时已晚。这是需要在调用者级别建立的东西。

您想要的是间接接受该变量(例如指向指针的指针),以便您可以操作它:

void list_remove_entry(struct node** head, struct node* entry)
{
  // Use the "indirect" argument directly
  while(*head != entry) {
    indirect = &(*head)->next;
  }

  // Updates caller's variable
  *head = (*head)->next;
}

现在你正确地称呼它:

list_remove_entry(&head, head);

值得注意的是,如果你可以给出返回值,你也可以不使用双指针:

struct node* list_remove_entry(struct node* head, struct node* entry)
{
  while(head != entry) {
    head = head->next;
  }

  // Caller can update with this value
  return head->next;
}

现在调用它的位置如下:

head = list_remove_entry(head, head);

除此之外,您的 list_remove_entry 函数只能删除第一个元素。理论上它应该能够删除链中的任何元素。

【讨论】:

  • 您的上一个版本永远不会从列表中删除任何内容。它只是返回要删除的节点之后的节点。
  • @Barmar 添加了关于此的注释。这修复了原始代码,就像它做同样的事情一样,但实际上操纵了head
猜你喜欢
  • 2021-12-09
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2019-01-18
  • 1970-01-01
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多