IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> nginx高级数据结构---双向链表 -> 正文阅读

[数据结构与算法]nginx高级数据结构---双向链表

介绍

如下图,节点ngx_queue_s分别用2个指针连接起来,prev指向上一个节点,next指向下一个节点,尾节点的next指向头,头节点的prev指向尾节点。
在这里插入图片描述

使用

使用比较简单,但是需要一定的步骤:

步骤1、定义容器管理链表

	ngx_queue_t queue_container;
    ngx_queue_init(&queue_container);

步骤2、自定义节点包含数据,并定义ngx_queue_t 成员,与1中定义的容器关联起来

自己定义节点需要包含一个ngx_queue_t的成员,位置随意,下面是一个示例。

struct {
    u_char* name;
    ngx_queue_t qEle;
    int age;  
} StudentNode;

步骤3、增删改查

链表定义和实现在ngx_queue.h与ngx_queue.c中,我们可以从中找到一些方法来使用,先定义一个包含5个节点的数组用以测试

StudentNode testNode[5] = {
        {"n1",{NULL,NULL},1},
        {"n2",{NULL,NULL},2},
        {"n3",{NULL,NULL},3},
        {"n4",{NULL,NULL},4},
        {"n5",{NULL,NULL},5}
    };

在ngx_queue.h中用宏定义实现了三种添加节点的方法,分别是添加头和尾,第一个参数代表链表容器(步骤1中定义),第二个参数代表插入的ngx_queue_t 节点(步骤二中定义的StudentNode的ngx_queue_t 成员);
在这里插入图片描述
三种方法都尝试插入,读者可以自己思考添加完后的数据顺序

  	ngx_queue_insert_head(&queue_container,&testNode[0].qEle);   
    ngx_queue_insert_after(&queue_container,&testNode[1].qEle);
    ngx_queue_insert_tail(&queue_container,&testNode[2].qEle);
    ngx_queue_insert_head(&queue_container,&testNode[3].qEle);   
    ngx_queue_insert_after(&queue_container,&testNode[4].qEle);

这是添加完后的打印:
在这里插入图片描述

删除

在这里插入图片描述

针对的是用户自定义数据,比如StudentNode 的成员name和age,与nginx链表无关

插入数据后整个容器的数据结构如下,双向链表的结构不变,多了一个StudentNode的成员指向链表的节点
在这里插入图片描述
链表可以通过前后的指针来遍历每一个节点,那么如何能通过查找链表的节点从而找到StudentNode呢?
在nginx_queue.h中,用宏定义了一个这样的函数,可以根据链表节点来获取用户定义的节点

#define offsetof(s,m) ((size_t)&(((s*)0)->m))
#define ngx_queue_data(q, type, link)  (type *) ((u_char *) q - offsetof(type, link))

一堆变量可能看的有点晕,我来从结果反推,先提供一个使用案例,如下,遍历获取StudentNode

//遍历打印 双向链表
void printQueue(ngx_queue_t* ngx_queue){
    
    for(ngx_queue_t *n = ngx_queue_head(ngx_queue);
    n != ngx_queue_sentinel(ngx_queue);
    n =ngx_queue_next(n))
    {
        StudentNode* bNode = ngx_queue_data(n, StudentNode, qEle); 
        printf("age %d name:%s\n",bNode->age,bNode->name);
    }
}

带入自己定义的变量直接将ngx_queue_data(n, StudentNode, qEle)展开宏

(StudentNode)*((u_char *)n-offsetof(StudentNode,qEle))

这样就会看到清楚点,其中 offsetof(s,m)为

((size_t)&(((StudentNode*)0)->qEle)

相当于结构体 StudentNode 中 qEle 地址的偏移量,也就是ngx_queue_t 在用户自定义节点中的地址偏移,用图可以这样表示,
在这里插入图片描述
反过来当我们知道qEle的地址就能知道StudentNode节点的地址,再来看展开的宏,这里的n指向的就是qEle的地址

(StudentNode)*((u_char *)n-offsetof(StudentNode,qEle))

在这里插入图片描述

总结

nginx提供了一个与数据无关的双向链表,同时提供了一个方法去访问用户定义的数据(当然,用户定义的数据必须包含ngx_queue_t 成员)

参考了《深入理解nginx模块开发与架构解析》

源码验证

说明

编译源码可以单独拎出来,也可以直接加入nginx源码目录,我是加入到源码目录
在这里插入图片描述
如要使用本来提供的源码,编译前请先编译nginx所有源码

编译指令

gcc -o ngx_queue_main ngx_queue_main.c -I ../src/core/ -I ../objs/  -I ../src/os/unix/ ../objs/src/core/ngx_queue.o

测试源码

#include <ngx_queue.h>
#include <stdio.h>

//编译指令
//gcc -o ngx_queue_main ngx_queue_main.c -I ../src/core/ -I ../objs/  -I ../src/os/unix/ ../objs/src/core/ngx_queue.o
typedef struct {
    u_char* name; 
    ngx_queue_t qEle;
    int age;  
} StudentNode;

//遍历打印 双向链表
void printQueue(ngx_queue_t* ngx_queue){
    
    for(ngx_queue_t *n = ngx_queue_head(ngx_queue);
    n != ngx_queue_sentinel(ngx_queue);
    n =ngx_queue_next(n))
    {
        StudentNode* bNode = ngx_queue_data(n, StudentNode, qEle); 

        printf("age %d name:%s\n",bNode->age,bNode->name);
    }
}

int main(){
    
    ngx_queue_t queue_container;
    ngx_queue_init(&queue_container);


    printf("pointer char size %d \n",sizeof(char*));
    StudentNode testNodea = {"dmj",{NULL,NULL},1};

    printf("pointer %d\n",testNodea.age);

    StudentNode testNode[5] = {
        {"n1",{NULL,NULL},1},
        {"n2",{NULL,NULL},2},
        {"n3",{NULL,NULL},3},
        {"n4",{NULL,NULL},4},
        {"n5",{NULL,NULL},5}
    };
    //增
    ngx_queue_insert_head(&queue_container,&testNode[0].qEle);   
    ngx_queue_insert_after(&queue_container,&testNode[1].qEle);
    ngx_queue_insert_tail(&queue_container,&testNode[2].qEle);
    ngx_queue_insert_head(&queue_container,&testNode[3].qEle);   
    ngx_queue_insert_after(&queue_container,&testNode[4].qEle);
    printf("------  add ------\n");
    printQueue(&queue_container);

    //删
    ngx_queue_remove(&testNode[0].qEle);   
    printf("------  delete n1------\n");
    printQueue(&queue_container);

    //改
    testNode[2].age = 11;
    printf("------  modify n3------\n");
    printQueue(&queue_container);

    
    ngx_queue_t* mid = ngx_queue_middle(&queue_container);
    StudentNode *mid_node = ngx_queue_data(mid, StudentNode, qEle); 
    printf("------ mid  ------\n");
    printf("------ %s,%d  ------\n",mid_node->name,mid_node->age);
    return -1;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:28:41  更:2022-05-24 18:30:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:28:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码