首页 > 二维指针操作链表

二维指针操作链表

背景

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 
#include typedef struct Node {struct Node *next;int data;
} node;void initList(node **head, unsigned int size)
{node **cur = head;for (int i=0; idata = 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: 
");node *cur = head;while(NULL != cur){printf("%X ", cur);cur = cur->next;}		}printf("

");node *cur = head;printf("list data: 
");while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("

");printf("delete node 3 from list

");deleteList(&head, 3);cur = head;printf("list data: 
");while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("

");printf("free list

");freeList(&head);cur = head;printf("list data: 
");	while(NULL != cur){printf("%d ", cur->data);cur = cur->next;}printf("
");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; idata = 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,也就没有什么问题了。

更多相关:

  • 给定一个整数 n, 返回从 1 到 n 的字典顺序。 例如, 给定 n =13,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] 。 请尽可能的优化算法的时间复杂度和空间复杂度。 输入的数据 n 小于等于 5,000,000。 根据题目描述,所谓字典顺序,即数值按照类似字符串首字母的ASCII大小进行排序...

  • 二叉树递归遍历存在的问题 如果我们的二叉树只有左子树,而且树的高度还很深的时候,这个时候递归调用遍历的时候,栈帧空间开辟的较大,很可能造成栈溢出。但是我们一个程序中,为堆分配的空间要比栈大的多,这个时候我们可以实现一个二叉树遍历的非递归的实现形式,模拟栈帧调用的过程,自己模拟一个栈用于保存节点,然后实现遍历。 非递归实现树的遍历...

  • 题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3085 题意:求n(<=10^100)之内最大的反素数。 思路: 优化2:   int prime[]= {1, 2, 3, 5, 7,11, 13, 17, 19, 23,29, 31, 37, 41,...

  • Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 351    Accepted Submission(s): 124 Problem Description 小度和...

  • 二叉搜索树的编码和解码描述: 编码:即将一个二叉搜索树编码,节点数值转换为字符串 解码:即将一个字符串解码,数值转换为对应的二叉搜索树的节点 过程导图如下: 针对性编码实现如下: /*数字转字符串*/ void change_num_to_string(int val, string &tmp) {string buf;whil...

  • 二叉搜索树又名二叉排序树。 大概简略的思维导图如下,方便记忆特性 基本二叉搜索树创建过程如下 /*数据结构如下*/ typedef struct tree {int data;struct tree *left = NULL;struct tree *right = NULL; }Tree,*TreeNode;/*Node 为二...

  • Linux安装Nodejs       阿里云镜像: https://npm.taobao.org/mirrors/node/ 选择所需版本,进行下载。    我这边下载的是:https://npm.taobao.org/mirrors/node/v8.2.1/node-v8.2.1-linux-x64.tar.gz         ...

  • 1. HDFS Architecture 一种Master-Slave结构。包括Name Node, Secondary Name Node,Data Node Job Tracker, Task Tracker。JobTrackers: 控制全部的Task Trackers 。这两个Tracker将会在MapReduce课程里...

  • 下载Nodejs插件,下载zip压缩包后解压链接: http://pan.baidu.com/s/1hsBk60k 密码: jrcv打开Sublime Text3,点击菜单“首选项(N)” =>“浏览插件(B)”打开“Packages”文件夹,并将第1部的Nodejs文件夹剪切进来打开文件“Nodejs.sublime-build”,...

  • 题目:复杂链表的复制 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。 示例 1: 输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出...

  • 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。 你应当保留两个分区中每个节点的初始相对位置。 示例: 输入: head = 1->4->3->2->5->2, x = 3 输出: 1->2->2->4->3->5 使用双指针的方式,各自构造一个大元素的头节点和小元素...

  • 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 说明:不允许修改给定的链表。 示例 1: 输入:head = [3,2,0,-4], pos = 1...

  • 题目描述如下: 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 说明: 1 ≤ m ≤ n ≤ 链表长度。 示例: 输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL 很明显这个题目是206 反转链表的进阶版 需要记录第m-1个节点和第n+1个...

  • 描述如下: 反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 方法一:原地反转 数据结构如下 struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}...