[英]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;
}
}
請說明以下內容:
- lpp =&(* lpp)-> next,我可以寫成lpp = lpp-> next,這不一樣嗎?
- * lpp =(* lpp)->下一個
最重要的是,我看不到此函數如何從列表中刪除結構節點
lpp
指向列表的第一個元素或某個元素的next
指針。
通過*lpp = (*lpp)->next
您可以將其直接寫入內存。 例如考慮清單
| el0 | -> | el1 | -> | el2 | -> NULL
list list->next
您代碼中的list
指向el0
和lpp = &list
。
現在,有兩種情況:
el0
匹配i
:-> list
變成|el0|.next
,即el1
。 運行此功能后,您將| el1 | -> | el2 | -> NULL list list->next
| el1 | -> | el2 | -> NULL list list->next
elX
匹配i
( X>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.