这两天偶遇无线驱动中对链表节点删除的问题,刚开始修改代码的时候并没有很在意,把TAILQ链表当成一般的链表来处理,虽然修改以后没有出现段错误,但是后面review代码的时候发现,这样改不对。后面花了点时间好好看了一下TAILQ的相关代码。

首先看一下这个TAILQ链表的结构,TAILQ链表包括两个部分,一个叫HEAD的头部,另外一个就是ENTRY实体部分;下面是两部分的定义:

#define TAILQ_HEAD(name, type)                      \

struct name {                               \

struct type *tqh_first; /* first element */         \

struct type **tqh_last; /* addr of last next element */     \

TRACEBUF                            \

}

#define TAILQ_ENTRY(type)                       \

struct {                                \

struct type *tqe_next;  /* next element */          \

struct type **tqe_prev; /* address of previous next element */  \

TRACEBUF                            \

}

其实HEAD跟ENTRY的定义是相同的,只是后面用户在定义自己数据的时候会将ENTRY放到自己定义的数据的头部;举个例子:

struct ieee80211_probe_entry

{

/*

* list element for linking on probe_list

*/

TAILQ_ENTRY(ieee80211_probe_entry)     ae_list;

/*

* list element for linking on probe_hash list

*/

LIST_ENTRY(ieee80211_probe_entry)      ae_hash;

u_int8_t                             ae_macaddr[IEEE80211_ADDR_LEN];

int                                                   ae_5g; //mark 5g request

int                                                   ae_2g;//mark 2.4g request

os_timer_t                                            assoc_block_timer;

int                                                   block_timer_init;

int                                                   block;

int                                                   refuse;

//christian added

int                                          rssi_sum[RECORD_RSSI_NUM];

u32                                        rssi_cnt;

systime_t                                                        last_update;

};

下面看一下这个TAILQ链表结构:

  简单画了三个节点,从上面可以看出,HEAD的tqh_first始终指向第一个元素,而tqh_last始终指向最后一个元素的tqe_next,而tqe_next的地址也是该节点的起始地址,所以tqh_last也可以看做指向最后一个元素的起始地址。这个地方有点纠结,因为struct type *tqh_first;tqh_first是一个一般的指针,而 struct type **tqh_last;tqh_last是一个二重指针,是一个指向指针的指针。当时在这也花了一点时间来想这个问题,可能是因为对二重指针理解不够深入。指针说白了就是一个地址,不管你是几重的,从面图我们就可以清晰的看到,如果将tqe_prev指针进行一个强制转换,其实我们就能够得到上一个元素的起始地址。也许这个就是queue的高明之处。这个是我遇到的一个觉得有点意思的地方,另外一个就是对链表节点删除的时候,也发现了一个比较有意思的问题。

删除节点的时候会用到一个循环,在queue.h里面有两个类似的循环。

项目中用到的是TAILQ_FOREACH,用这个宏的时候,在删除的时候我需要去找到它的上一个节点的地址,并将其保存起来,这个找上一个节点的地址就用到前面说的,通过tqe_prev来找,通过这种方式查找上一个节点,找到的节点很有可能是头部。

#define    TAILQ_FOREACH(var, head, field)                    \

for ((var) = TAILQ_FIRST((head));                \

(var);                            \

(var) = TAILQ_NEXT((var), field))

第二种就是TAILQ_FOREACH_SAFE这个宏,用这个宏的好处就不需要去找将要被删除节点的上一个节点。这个宏的做法是在它第一次执行for里面语句的时候就取到了下一个节点,执行完完语句以后就只是简单的进行了一个赋值操作,这样就能够保证for循环能够遍历到真个链表,而且不会像上面那种情况会出现节点为头部的情况。这也是这种写法比较高明之处。

#define    TAILQ_FOREACH_SAFE(var, head, field, tvar)            \

for ((var) = TAILQ_FIRST((head));                \

(var) && ((tvar) = TAILQ_NEXT((var), field), 1);        \

(var) = (tvar))

宏里面的这一句我觉得写的非常不错, (var) && ((tvar) = TAILQ_NEXT((var), field), 1);一般情况下我们只需要写一个var就可以了,但是在这个地方后面加上了((tvar) = TAILQ_NEXT((var), field), 1)这样一句,var为空的时候,for循环结束,var不为NULL的时候,那后面肯定必须为真了,而后面这一句也恰好为真,这一条语句做了两个事情,第一取到下一个节点,第二是整个表达式值为1,使得for循环得以继续。

如果不仔细去看这两个宏的区别,确实不能体会到TAILQ_FOREACH_SAFE,safe在哪里。;

首先我们来看看通过这两个循环来删除节点的代码格式:

TAILQ_FOREACH:

Struct type * temp_var;

TAILQ_FOREACH(var, head, field){

If(var ….)//满足我们的条件{

Temp_var =(struct type *) var->tqe_prev;//保存上一个节点的地址;

TAILQ _FREE(var);//free 掉节点信息;

}

Var = Temp_var;//从新赋值给var;

}

TAILQ_FOREACH_SAFE:

Struct type * temp_var;

TAILQ_FOREACH(var, head, field,temp_var){

If(var ….)//满足我们的条件{

TAILQ _FREE(var);//free 掉节点信息;

}

}

对比一下两段代码,会发现第二段确实比较好一些。

关于TAILQ链表节点删除问题的更多相关文章

  1. LeetCode OJ:Delete Node in a Linked List(链表节点删除)

    Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...

  2. linux内核之链表操作解析

    本文只是对linux内核中的链表进行分析.内核版本是linux-2.6.32.63.文件在:linux内核/linux-2.6.32.63/include/linux/list.h.本文对list.h ...

  3. [LeetCode 总结帖]: 链表专题

    链表在笔试面试中都是出镜率极高的一种数据结构. 由于链表具有结构简单,代码量较少,变化多,可以较为全面的考察应聘者的逻辑思考能力以及应变能力的特点,而备受面试官青睐. 在本节中,我将Leetcode中 ...

  4. Python—数据结构——链表

    数据结构——链表 一.简介 链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构.由一系列节点组成的元素集合.每个节点包含两部分,数据域item和指向下一个节 ...

  5. JDK1.8 HashMap 源码分析

    一.概述 以键值对的形式存储,是基于Map接口的实现,可以接收null的键值,不保证有序(比如插入顺序),存储着Entry(hash, key, value, next)对象. 二.示例 public ...

  6. C/C++面试题总结

    腾讯阿里面试题总结:1. 多态机制2. 排序算法(快排.堆排)3. 程序内存分配4. unix多线程5. 哈希查找6. oop特点7. 素数(优化)8. 字符串掩膜操作(内存紧凑)9. 多边形相交10 ...

  7. C语言实现penna模型

    一年前写的代码,偶然翻出来.发现自己当时水平还不赖吗. # include <stdio.h> # include <stdlib.h> # include <time. ...

  8. JAVA 链表操作:循环链表

    主要分析示例: 一.循环链表简述 二.单链表循环链表 三.双链表循环链表 一.循环链表简述 循环链表即链表形成了一个循环的结构,尾节点不再指向NULL,而是指向头节点HEAD,此时判定链表的结束是尾节 ...

  9. C语言 Linux内核链表(企业级链表)

    //Linux内核链表(企业级链表) #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> ...

随机推荐

  1. 安装nova

    在控制节点上执行 controllerHost='controller' controllerIP='172.31.240.49' MYSQL_PASSWD='m4r!adbOP' RABBIT_PA ...

  2. 【并行计算-CUDA开发】__syncthreads的理解

    __syncthreads()是cuda的内建函数,用于块内线程通信. __syncthreads() is you garden variety thread barrier. Any thread ...

  3. java 8 Base64用法

    Java 8的java.util套件中,新增了Base64的类别,可以用来处理Base64的编码与解码,用法如下: final Base64.Decoder decoder = Base64.getD ...

  4. flask内置的信号

    from flask import Flask,request,template_rendered,render_template,got_request_exception from signals ...

  5. Flink与其他流处理方式的对比分析

    一.Flink与其他流处理方式的对比分析 ·1.Flink特点: Apache Flink 是一个开源的分布式,高性能,高可用,准确的流处理框架. 主要由 Java 代码实现. 支持实时流(strea ...

  6. tcpdump移植和使用

    转载于:http://blog.chinaunix.net/uid-30497107-id-5757540.html?utm_source=tuicool&utm_medium=referra ...

  7. PTA(Advanced Level)1075.PAT Judge

    The ranklist of PAT is generated from the status list, which shows the scores of the submissions. Th ...

  8. *【Python】【demo实验31】【练习实例】【使用turtle画小猪佩奇】

    如下图小猪佩奇: 要求使用turtle画小猪佩奇: 源码: # encoding=utf-8 # -*- coding: UTF-8 -*- # 使用turtle画小猪佩奇 from turtle i ...

  9. centos7 源码安装 MongoDb

    1.下载源码包 curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.12.tgz 2.解压 放到 /usr/local/ ...

  10. 【sublime Text】关闭sublime的更新提醒和激活提醒

    下载了原版的sublime Text,未激活的,每次启动都会提醒要去更新么?需要激活吧 ? 超级烦.[谁让没有激活呢?] 那没办法 ,激活吧! Help ---- Enter License--> ...