二维指针操作链表
背景
Linus slashdot: ? ?https://meta.slashdot.org/story/12/10/11/0030249
Linus大嬸在slashdot上回答一些編程愛好者的提問,其中一個人問他什么樣的代碼是他所喜好的,大嬸表述了自己一些觀點之后,舉了一個指針的例子,解釋了什么才是core low-level coding。
下面是Linus的教學原文及翻譯——
“At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I’ve seen too many people who delete a singly-linked list entry by keeping track of the “prev” entry, and then to delete the entry, doing something like。(在這段話的最后,我實際上希望更多的人了解什么是真正的核心底層代碼。這并不像無鎖文件名查詢(注:可能是git源碼里的設計)那樣龐大、復雜,只是僅僅像諸如使用二級指針那樣簡單的技術。例如,我見過很多人在刪除一個單項鏈表的時候,維護了一個”prev”表項指針,然后刪除當前表項,就像這樣)”
if (prev)prev->next = entry->next;elselist_head = entry->next;
and whenever I see code like that, I just go “This person doesn’t understand pointers”. And it’s sadly quite common.(當我看到這樣的代碼時,我就會想“這個人不了解指針”。令人難過的是這太常見了。
我嘗試用二維指針操作鏈表,寫了如下code。
希望領悟到二維指針的精髓。
#include <stdio.h>
#include <stdlib.h>typedef struct Node {struct Node *next;int data;
} node;void initList(node **head, unsigned int size)
{node **cur = head;for (int i=0; i<size; i++){*cur = (node *)malloc(sizeof(node));(*cur)->data = i;(*cur)->next = NULL;cur = &(*cur)->next; }
}void deleteList(node **head, int data)
{for (node **cur=head; *cur;){node *entry = *cur;if (entry->data == data){*cur = entry->next;free(entry);entry = NULL;}elsecur = &entry->next;}
}void freeList(node **head)
{for (node **cur=head; *cur;){node *entry = *cur;*cur = entry->next;free(entry);entry = NULL;}
}int main()
{node *head = NULL;unsigned int size = 5;initList(&head, 5);if (NULL == head){printf("head is null, init list failed");return -1;}else{printf("init list suc, addr is: \n");node *cur = head;while(NULL != cur){printf("%X ", cur);cur = cur->next;} }printf("\n\n");node *cur = head;printf("list data: \n");while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("\n\n");printf("delete node 3 from list\n\n");deleteList(&head, 3);cur = head;printf("list data: \n");while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("\n\n");printf("free list\n\n");freeList(&head);cur = head;printf("list data: \n"); while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("\n");return 0;
}
運行結果如下:
init list suc, addr is:?
4B8260 4B8280 4B82A0 4B82C0 4B82E0?
list data:?
0 1 2 3 4?
delete node 3 from list
list data:?
0 1 2 4?
free list
list data:?
Q1: 如果我們用下面的initList_1來代替原來的initList,重新build,運行,你想想會發生什么?
list 能初始化成功嗎?
void initList_1(node **head, unsigned int size)
{node **cur = head;for (int i=0; i<size; i++){node *entry = *cur;entry = (node *)malloc(sizeof(node));entry->data = i;entry->next = NULL;cur = &entry->next; }
}
A:list不能初始化成功,*head 并沒有指向分配的內存,依然為NULL, entry 指向的內存并不能返回到函數外部。
Q2: 上面的freeList 函數是否把list中分配的內存在free后設置成了NULL,杜絕野指針?
void freeList(node **head)
{for (node **cur=head; *cur;){node *entry = *cur;*cur = entry->next;free(entry);entry = NULL; /* is this lie useful? avoid wild pointer ?*/}
}
A: No, 內存free后并沒有被置為NULL.?
其實在循環過程中,*head(也就是*cur) 一直在鏈表中后移,最后指向了最后一個node的next 指針,也就是為NULL。
所以在這個函數執行完后,list的首節點指向了NULL(最后一個node的next 指針),而其它的node所分配的內存雖然被free了,但是并沒有設置為NULL。這樣做也沒有問題,list暴露給用戶的就是head 指針,非head節點并沒有提供給用戶,沒有置為NULL,也就沒有什么問題了。
總結
- 上一篇: 连续地址数据(数组或者malloc的内存
- 下一篇: memset初始化内存