简体   繁体   English

无分支链表删除 C 中的条目

[英]Branchless linked list remove entry in C

In this TED talk , Torvalds proposes a remove entry function without if conditions.这个 TED 演讲中,Torvalds 提出了一个不带 if 条件的删除条目 function。 I've tried to emulate in the code below, but it doesn't work if the entry to remove is the head of the list.我尝试在下面的代码中进行模拟,但如果要删除的条目是列表的头部,则它不起作用。 1.) Why does this fail to remove the head specifically? 1.)为什么这不能专门去除头部? 2.) Doesn't this method generate a memory leak since we never free the entry? 2.) 由于我们从未释放条目,此方法不会产生 memory 泄漏吗?

/** 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;
}

The code in the TED talk doesn't take head as a parameter, it's a global variable. TED 演讲中的代码没有将head作为参数,它是一个全局变量。

As a result, when it does结果,当它发生

*indirect = entry->next;

it will modify the head variable if the entry to be removed is equal to head , because the while loop stops immediately and indirect still contains &head .如果要删除的条目等于head ,它将修改head变量,因为while循环立即停止并且indirect仍然包含&head

This doesn't work the same when you make head a parameter, because now you're just modifying the local variable, not the caller's variable.当您将head作为参数时,这并不相同,因为现在您只是在修改局部变量,而不是调用者的变量。 See Changing address contained by pointer using function for how you can redesign the function to solve this (it's also in @tadman's answer).有关如何重新设计 function 来解决此问题,请参阅使用 function 更改指针包含的地址(这也在 @tadman 的回答中)。

In answer to your second question, yes, this will create a memory leak.在回答您的第二个问题时,是的,这将产生 memory 泄漏。 Linus's example is just meant to illustrate one particular aspect of the two ways to code this function, so he left out everything unrelated to that difference. Linus 的示例只是为了说明编码此 function 的两种方法的一个特定方面,因此他省略了与该差异无关的所有内容。 You solve this by replacing the assignment with:您可以通过将作业替换为以下内容来解决此问题:

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

Note that he also left out error checking.请注意,他还遗漏了错误检查。 His code assumes that the entry will be found, so he never checks for *indirect == NULL before dereferencing it (and my above assignment doesn't check the double indirection).他的代码假定会找到该条目,因此他在取消引用之前从不检查*indirect == NULL (并且我的上述分配不检查双重间接)。

It's not a coding lesson, it's just a simple example that needs to fit on a slide.这不是一堂编码课,它只是一个需要放在幻灯片上的简单示例。

You're doing a lot of voodoo programming here:你在这里做了很多巫毒编程:

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;
}

If you didn't get an indirect variable to start with, it's too late to turn it into one inside a function.如果您一开始没有获得间接变量,那么在 function 中将其转换为一个为时已晚。 This is something that needs to be established at the caller level.这是需要在调用者级别建立的东西。

What you want is to accept that variable indirectly (eg a pointer to a pointer) so you can manipulate it:您想要的是间接接受该变量(例如指向指针的指针),以便您可以操作它:

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;
}

Now you call it properly:现在你正确地调用它:

list_remove_entry(&head, head);

It's worth noting you can also do this without the double pointer if you can give a return value:值得注意的是,如果您可以提供返回值,您也可以在没有双指针的情况下执行此操作:

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;
}

Where calling it now looks like:现在调用它的位置如下所示:

head = list_remove_entry(head, head);

That aside, your list_remove_entry function can only remove the first element.除此之外,您的list_remove_entry function 只能删除第一个元素。 Theoretically it should be able to remove any element in the chain.从理论上讲,它应该能够删除链中的任何元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM