簡體   English   中英

使用指針到指針刪除結構節點

[英]Delete struct node using pointer-to-pointer

假設我有一個鏈表,下一個函數從鏈表中刪除結構節點

struct list **lpp;
for (lpp = &list; *lpp != NULL; lpp = &(*lpp)->next)
{
    if ((*lpp)->item == i)
    {
        *lpp = (*lpp)->next;
        break;
    }
}

請說明以下內容:

  1. lpp =&(* lpp)-> next,我可以寫成lpp = lpp-> next,這不一樣嗎?
  2. * lpp =(* lpp)->下一個

最重要的是,我看不到此函數如何從列表中刪除結構節點

lpp指向列表的第一個元素或某個元素的next指針。

通過*lpp = (*lpp)->next您可以將其直接寫入內存。 例如考慮清單

| el0 | -> | el1 | -> | el2 | -> NULL
 list     list->next

您代碼中的list指向el0lpp = &list

現在,有兩種情況:

  • el0匹配i :-> list變成|el0|.next ,即el1 運行此功能后,您將| el1 | -> | el2 | -> NULL list list->next | el1 | -> | el2 | -> NULL list list->next
  • elX匹配iX>0 ): lpp&el_{X-1}.next ,通過*lpp = ... ,該.next將指向elX.next 例如,假設el1匹配,則得到| el0 | -> | el2 | -> NULL | el0 | -> | el2 | -> NULL

lpp = &(*lpp)->next用於獲取next的引用。 簡單的lpp = lpp->next並不足夠,因為它是不同的類型。 當您處理lpp->next*lpp就像*lpp->next ,它將取消引用下一個元素的內容。

單列表操作

雖然與這個問題無關,但是由於其他討論,還有更多代碼...

假設數據結構像

struct node {
    int data;
    struct node *next;
};

在實際代碼中, data將不是該節點的成員,而struct node將是另一個對象內的混合,並且使用container_of東西來訪問它。 但是對於這個問題,請保持上面的狀態...

我們可以定義一些函數,例如

void slist_add(struct node *node, struct node *root)
{
    node->next = root->next;
    root->next = node;
}

void slist_remove(struct node **node)
{
    if (node)
        *node = (*node)->next;
}

struct node **slist_search(struct node *root, int key)
{
    struct node **ptr;

    for (ptr = &root->next; *ptr; ptr = &(*ptr)->next) {
        if ((*ptr)->data  == key)
            return ptr;
    }

    return NULL;
}

然后,我們使用一個空的struct node作為錨點:

int main(void)
{
    struct node head = { .next = NULL };

    /* add a node */
    {
        struct node *n = malloc(sizeof *n);
        n->data = 23;

        slist_add(n, &head);
    }

    /* add a node */
    {
        struct node *n = malloc(sizeof *n);
        n->data = 42;

        slist_add(n, &head);
    }

    /* verify our expectations... */
    assert(head.next != NULL);
    assert(head.next->data == 42);

    assert(head.next->next != NULL);
    assert(head.next->next->data == 23);
    assert(head.next->next->next == NULL);

    /* remove the node */
    {
        struct node **ptr = slist_search(&head, 42);

        assert(ptr != NULL);
        assert(*ptr != NULL);
        assert((*ptr)->data == 42);

        if (ptr) {
           struct node *n = *ptr;
           slist_remove(ptr);
           free(n);
        }
    }

    /* remove the node */
    {
        struct node **ptr = slist_search(&head, 23);

        assert(ptr != NULL);
        assert(*ptr != NULL);
        assert((*ptr)->data == 23);

        if (ptr) {
           struct node *n = *ptr;
           slist_remove(ptr);
           free(n);
        }
    }

    assert(head.next == NULL);
}

您的代碼是極其簡化且不完整的節點刪除嘗試。
您必須照顧好邊緣情況並實際free內存。

這行:

  *lpp = (*lpp)->next; 

負責從列表中taking out節點。 僅當*lpp是列表頭並且列表上還有另一個元素時,它才有效。 *lpp指向不再需要的節點,並由列表中的下一個節點替換

 (*lpp)->next;

lpp = &(*lpp)->next ,我可以寫成lpp = lpp->next ,這不一樣嗎?

不它不是。 並且lpp = lpp->next將不會編譯。

&是取消引用運算符。 它正在獲取節點指針的地址。 您可以將這一行寫為

lpp = & ( (*lpp)->next ); 

並且您可以將(*lpp)->next識別為列表中的下一個節點指針。

lpp是指向指針的指針。 *lpp->next是編譯器已知的表達式,而不是lpp->next

我想你誤會了
lpp = & ( (*lpp)->next );

lpp = &* (lpp->next); 

並認為&*會自行取消。

如果要刪除列表中間的節點,則必須將要刪除的節點之前存在的節點連接到標記為刪除的節點之后的節點。

類似於:

         prev = current;                       
         to_free = current->next;         // node to be freed  

         prev->next = to_free->next;      // connect nodes before deletion        
         free(to_free)

您能告訴我如何使用雙重懲罰者刪除鏈接列表節點嗎? – Fela93

我已經添加了刪除節點的測試程序:

#include <stdio.h>
#include <stdlib.h>

// Basic simple single list implementation to illustrate 
// a proper deletion of the node which has a specfic data value.

// Node in List
typedef struct node {
    int data;
    struct node* next; // pointer to next node
}node;

// returns newly created node
node* node_new(int data)
{
    node* new_node = malloc(sizeof(node)); // allocate memory for the node 

    if (new_node == NULL)
        return NULL;                             // protection 

    new_node->data = data;                       // remember the data 
    new_node->next = NULL;                       // no next node

    return new_node;                             // return new created node
}

// The method creates a node and prepends it at the beginning of the list.
// 
// Frequently used names for this method:
// 
// insert at head
// add first
// prepend
//
// returns new head or NULL on failer

node* add_node(node **head, node* new_node)

{
   // Add item to the front of the in_list, return pointer to the prepended node (head)    

    if(head == NULL)
        return NULL;

    if(new_node == NULL)                         // problem, not enough memory
       return NULL;                              // in_list->head has not changed 

/* 
                 new_node      
                   |*| -->  NULL   
                   next        
*/       
    if(*head == NULL)                    // if list is empty 
    {
        *head = new_node;                // the new_node becomes a head   
    }
    else // list already have a head node
    {
/*
                |2|-->|1|-->NULL
                 ^ 
                 |
                 *
                head (2) (list pointer)
*/
        new_node->next = *head;         // now, the new node next pointer points to the node pointed by the list head, see below:       
/* 
          new_node     
            |3|-->     |2|-->|1|-->NULL
                        ^  
                        |
                        *
                       head (list pointer)
*/          
        *head = new_node;               // the list head has to move to new_node ( a new prepanded node)  
 /* 
          new_node       
            |3|-->  |2|-->|1|-->NULL
             ^       
             |           
             *           
            head (3) (list pointer)
*/         
    }
    return *head;                       // we are returning pinter to new_node
}

// Print out list
void print_nodes(node** head)
{
    node* node;

    if (head == NULL) {
        return;
    }

    if (*head == NULL){
        printf("List is empty!\n");
        return;
    }

    printf("List: ");
    node = *head;

    while(node != NULL)
    {
        printf(" %d", node->data);

        node = node->next;
    }

    printf("\n");
}

struct node *find(struct node *start, int data)             // find p to be removed
{
    node* node;

    if (start == NULL)
        return NULL;

    node = start;

    while(node != NULL)
    {
        if (node->data == data)
            return node; 

        node = node->next;
    }

    return NULL;
}

int delete(struct node **start, int data)
{
     struct node *p, *prev, *next, *to_free;

     if (start == NULL)                      // protection
        return 0;

     p = find(*start, data);                 // find element to be removed

     if (p == NULL)
        return 0;

     if (*start == NULL)
        return 0;                            // protection

     if(*start == p)                         // head == p
     {
        if((*start)->next !=NULL)
        {
            *start = (*start)->next;         // move head

            printf("Will be removed: %p\n",p);        
            free(p);                         // remove old head

            return 1;
        }
        else // the only node
        {
            free(p);                        // free the node pointed by *start (header)

            printf("Last node removed\n");
            *start = NULL;                  // header points to NULL 

            return 1;
        }
     }

     // p != start:

     next = *start; 
     while (next != NULL)
     {
        prev = next;                       
        to_free = next->next;                // candidate to be freed   

       if( to_free == p )
        {
            prev->next = to_free->next;      // connect nodes before deletion 

            free(to_free);                   // now free the remembered `next`
            to_free = NULL;                  // so it does not point to the released memory
            return 1;
        }

        next = next->next;                   // this node was not a match 
     } //while

    return 0; 
}

int main() {   
   node *head = NULL; 

   printf("head: %p\n", head);

   node *n1 = node_new(1);
   node *n2 = node_new(2);
   node *n3 = node_new(3);

   print_nodes(&head);

   add_node(&head, n1);
   add_node(&head, n2);
   add_node(&head, n3);

   printf("head points to:  %p\n", head);

  // list has 3 elements
   print_nodes(&head);

   delete(&head, 3);  
   print_nodes(&head);

   delete(&head, 1);  
   print_nodes(&head);

   delete(&head, 2);
   print_nodes(&head);

   printf("head points to: %p\n", head);

   print_nodes(&head);

  return 0;
}

輸出:

head: (nil)
List is empty!
head points to:  0x5617cd3802b0
List:  3 2 1
Will be removed: 0x5617cd3802b0
List:  2 1
List:  2
Last node removed
List is empty!
head points to: (nil)
List is empty!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM