二维指针操作链表

背景

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;

else

    list_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;
		}
		else
			cur = &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,也就没有什么问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值