编码基础一:侵入式链表

一、简介概述

1、普通链表数据结构

每个节点的next指针指向下一个节点的首地址。这样会有如下的限制:

  • 一条链表上的所有节点的数据类型需要完全一致。
  • 对某条链表的操作如插入,删除等只能对这种类型的链表进行操作,如果链表的类型换了,就要重新再封装出一套一样的操作,泛化能力差

2、侵入式链表数据结构

 节点的链接成员指向的是下一个节点的链接成员。使用侵入式链表的好处是:

  • 节点类型无需一致,只需要成员节点(element_t)包含list_node_t成员即可
  • 泛化能力强,所有链表的操作方式均可统一;
typedef struct list_node list_node_t;

//链表节点结构体定义
typedef struct list_node {
	list_node_t* prev;
	list_node_t* next;
} list_node_t;


//链表结构体定义
typedef struct list {
	int list_size;
	list_node_t head;
} list_t;


//链表成员结构体定义,重点需要包含list_node_t定义
typedef struct element {
	list_node_t list_node;
	int  element_type;
	int  element_size;
	char element_data[1];
} element_t;

二、详细介绍

侵入式链表中的节点只有地址信息,能够访问节点上的数据成员变量,主要靠两个核心函数: 

  • offsetof
  • container_of

1、offsetof

1)宏原型

#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
    #ifdef __cplusplus
        #define offsetof(s,m)  \
           ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
    #else
        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
    #endif
#else
    #define offsetof(s,m) __builtin_offsetof(s,m)
#endif

2)宏作用

计算结构体成员相对于结构体的偏移

3)参数说明

  • type:      结构体类型
  • member:结构体成员

4)原理分析

偏移 = 成员地址 - 结构体地址,若结构体地址为0,则偏移 = 成员地址;

5)应用示例

typedef struct element {
	list_node_t list_node;
	int  element_type;
	int  element_size;
	char element_data[1];
} element_t;


printf("offset: %zd %zd %zd\r\n", 
      offsetof(element_t, list_node), 
      offsetof(element_t, element_type), 
      offsetof(element_t, element_size));


//打印结果
offset: 0 16 20

2、container_of

1)宏原型

#define container_of(ptr, type, member) \
  ((type*)(((char*)((type*)(ptr))) - offsetof(type, member)))

2)宏作用

     通过结构体的成员,结构体成员的地址以及结构体的类型来获取结构体的首地址。

3)参数说明

  •     ptr:         结构体成员的地址
  •     type:      结构体类型
  •     member:结构体成员

4)原理分析

      结构体首地址 = 成员地址 - 成员偏移,成员偏移通过offsetof宏求出;

5)应用示例

int main()
{
	element_t element, *p_element;

	element.element_type = 1234;
	element.element_size = 5678;

	p_element = container_of(&element.list_node, element_t, list_node);

	printf("p_element->element_type :%d p_element->element_size :%d\n", 
		p_element->element_type, p_element->element_size);
}

p_element->element_type :1234 p_element->element_size :5678

3、侵入式链表 

介绍到这里,就可以理解面前第一章第2小节,介绍的节点类型无需一致,只需要成员节点(element_t)包含list_node_t成员即可。我们只要知道list_node_t成员地址,就可以通过offsetof=>container_of获取整个element_t的成员变量。

示例代码如下:

#include "list.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void ListInit(list_t* list) {
	list->list_size = 0;
	ListNodeInit(&list->head);
}

void ListAppend(list_t* list, list_node_t* node) {
	node->next = &list->head;
	node->prev = list->head.prev;
	node->prev->next = node;
	list->head.prev = node;
	list->list_size++;
}

void ListRemove(list_t* list, list_node_t* node) {
	ListNodeDetach(node);
	ListNodeInit(node);
	list->list_size--;
}

list_node_t* ListFirstGet(const list_t* list) {
	return !ListEmpty(list) ? list->head.next : NULL;
}

list_node_t* ListLastGet(const list_t* list) {
	return !ListEmpty(list) ? list->head.prev : NULL;
}

bool ListEmpty(const list_t* list) {
	return !ListEnlisted(&list->head);
}

void ListNodeInit(list_node_t* node) {
	node->prev = node;
	node->next = node;
}

void ListNodeDetach(list_node_t* node) {
	node->prev->next = node->next;
	node->next->prev = node->prev;
}

bool ListEnlisted(const list_node_t* node) {
	return node->prev != node;
}

list_t list_;

int main()
{
	list_node_t* list_node = NULL;
	ListInit(&list_);
	for (int i = 0; i < 15; i++) {
		element_t* element = (element_t*)malloc(sizeof(element_t) + sizeof(AI_UPLOAD_ALL_INFO_T));
		element->element_type = i;
		element->element_size = sizeof(AI_UPLOAD_ALL_INFO_T);
		ListAppend(&list_, &element->list_node);
		printf("push element :%d queue_size :%d\n", element->element_type, list_.list_size);

		list_node = ListLastGet(&list_);
		element_t* element1 = GetListNode(list_node, element_t);
		printf("QueueLastGet element :%d queue_size :%d\n", element1->element_type, list_.list_size);
	}

	printf("list_size :%d\n", list_.list_size);

	while ((list_node = ListFirstGet(&list_)) != NULL) {
		element_t* element = GetListNode(list_node, element_t);
		printf("pop element :%d queue_size :%d\n", element->element_type, list_.list_size);
		ListRemove(&list_, list_node);
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/89767.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

牛客网Verilog刷题 | 入门特别版本

文章目录 1、 VL1 输出12、VL2 wire连线3、 VL3 多wire连接4、VL4 反相器5、VL5 与门6、VL6 NOR 门7、VL7 XOR 门8、VL8 逻辑运算10、VL10 逻辑运算211、VL11 多位信号12、VL12 信号顺序调整13、VL13 位运算与逻辑运算14、VL14 对信号按位操作15、VL15 信号级联合并16、VL16 信…

安装Docker并配置镜像加速器、容器

1.安装docker服务&#xff0c;配置镜像加速器 安装软件包 [rootlocalhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 设置yum源 [rootlocalhost ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo…

【问题处理】解决Spring事务@Transactional多层嵌套失效

场景&#xff1a; 在 AService 中&#xff0c;我会直接调用 A 的数据操作层去操作 A的数据 以及 A关联密切的其它数据&#xff0c;在操作完之后&#xff0c;会去调用 BService 和 CService 中更新对应的数据&#xff0c;并在每个方法上使用了事务&#xff0c;但在调用 BService…

VMware 中Centos8的NAT网络设置

1、先将虚拟机设置为NAT模式 2、打开虚拟网络编辑器&#xff0c;记录以下信息 NAT设置&#xff1a;子网掩码、网关 DHCP设置&#xff1a;I P 范围 (自动时) 3、进入Centos8的网络设置页面&#xff0c;按照记录的信息进行配置 4、重载、重启网卡 nmcli c reload ensl60 n…

4G电力摄像机如何通过AT指令对接到国网平台呢?

对于针对电网安全运行的迫切需求&#xff0c;”输电线路智能可视化监测系统”被研发并应用&#xff0c;通过视频监控和AI智能分析技术&#xff0c;实现了对输电线路远程视频在线监测、外力破坏智能分析&#xff0c;可实现对输电线路的全天候实时监测和预警&#xff0c;有效保障…

element plus 的图片上传组件回显

element图片回显是通过修改file-list属性的url属性实现的。 <!-- 图片上传 --><el-form-item label"景区图片" prop"s_img"><el-uploadlist-type"picture-card":action"网址":on-change"handleChange":befor…

【KingSCADA】问题处理:记录KS历史报警查询异常

哈喽&#xff0c;大家好&#xff01;我是雷工。 本篇记录KingSCADA的历史报警应用中的一个问题&#xff0c;及处理过程。 一、问题描述 最近客户遇到这么一个问题&#xff1a;当打开历史报警窗界面&#xff0c;自动加载的报警信息中有显示最近几天的报警信息&#xff0c;但当…

[JavaWeb]【十二】web后端开发-事务管理AOP

目录 一、事务管理 1.1 事务回顾 1.2 Spring事务管理 1.2.1 案例 1.2.1.1 EmpMapper新增deleteByDeptId方法 1.2.1.2 DeptServiceImpl 1.2.1.3 启动服务-测试 1.2.2 模拟异常 1.2.3 分析问题 1.2.4 Spring事务管理&#xff08;一般用在类似多次delete&#xff09; 1.2.4…

【快速傅里叶变换(fft)和逆快速傅里叶变换】生成雷达接收到的经过多普勒频移的脉冲雷达信号(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Jupyter Notebook 配置根目录

注&#xff1a;本文是在 Windows 10 上配置 Jupyter Notebook 打开的默认根目录&#xff0c;Linux 同。 步骤一&#xff1a;创建 Jupyter Notebook 配置文件 使用以下命令创建 Jupyter Notebook 配置文件&#xff08;如果尚未创建&#xff09;&#xff1a; jupyter notebook …

SecureBridge安全文件下载的组件Crack

SecureBridge安全文件下载的组件Crack SecureBridge包括SSH、SSL和SFTP客户端和服务器组件。它使用SSH或SSL安全传输层协议和加密消息语法来保护任何TCP流量&#xff0c;这些协议为客户端和服务器提供身份验证、强数据加密和数据完整性验证。SecureBridge组件可以与数据访问组件…

Neo4j实现表字段级血缘关系

需求背景 需要在前端页面展示当前表字段的所有上下游血缘关系&#xff0c;以进一步做数据诊断治理。大致效果图如下&#xff1a; 首先这里解释什么是表字段血缘关系&#xff0c;SQL 示例&#xff1a; CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status F…

十五、systemctl命令如何使用?

在Linux系统中&#xff0c;一些内置服务可以通过systemctl控制&#xff0c;部分第三方软件也可以通过systemctl控制。 1、基础语法 start&#xff1a;开启服务&#xff1b; stop&#xff1a;关闭服务&#xff1b; status&#xff1a;查看服务当前状态&#xff1b; enable&a…

Centos 7.6 安装mongodb

以下是在CentOS 7.6上安装MongoDB的步骤&#xff1a; 打开终端并以root用户身份登录系统。 创建一个新的MongoDB存储库文件 /etc/yum.repos.d/mongodb-org-4.4.repo 并编辑它。 sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中&#xff0c;添加下面的内容到文件中并…

Vue中使用element-plus中的el-dialog定义弹窗-内部样式修改-v-model实现-demo

效果图 实现代码 <template><el-dialog class"no-code-dialog" v-model"isShow" title"没有收到验证码&#xff1f;"><div class"nocode-body"><div class"tips">请尝试一下操作</div><d…

智慧化工地SaaS平台源码,PC端+APP端+智慧数据可视化大屏端,源码完全开源不封装,自主研发,支持二开,项目使用,微服务+Java++vue+mysql

智慧工地管理平台充分运用数字化技术&#xff0c;聚焦施工现场岗位一线&#xff0c;依托物联网、互联网、AI等技术&#xff0c;围绕施工现场管理的人、机、料、法、环五大维度&#xff0c;以及施工过程管理的进度、质量、安全三大体系为基础应用&#xff0c;实现全面高效的工程…

CAD哪个版本最好用?学习一下CAD版本转换方法

CAD即计算机辅助设计&#xff0c;是一个制图软件&#xff0c;用于绘制建筑、机械、电子等领域的图纸。CAD文件通常被称为“图纸”或“工程图”。 CAD文件通常在以下方面使用&#xff1a; 1. 建筑&#xff1a;建筑师使用CAD文件来创建建筑物的平面图、立体图和剖面图。 2. 机…

Hugo托管到Github Pages

Github通过其Github Pages服务可以user、project或organization提供免费快速的静态托管&#xff0c;同时使用Github Actions自动化开发工作流和构建。 1.创建Github仓库 可见性为public。 命名为username.github.io&#xff0c;username为你的Github用户名。 2.添加远程仓库…

Hystrix: 服务降级

cloud是基础&#xff0c;eureka是服务注册和发现&#xff0c;consumer是消费者去消费provider里的东西&#xff0c;消费方式就是Feign和Ribbon&#xff0c;feign 接口消费&#xff0c;ribbon Rest消费 服务降级发生在客户端&#xff0c;客户端因为请求关闭的服务器&#xff0…

备份集中的数据库备份与现有的数据库不同?

数据已经成为公司的主要资产,特别是对于企业来说&#xff0c;数据库中存储的信息通常是其业务运营的核心。 因此&#xff0c;确保数据库的安全性和完整性至关重要。这导致数据库备份成为企业信息管理的重要组成部分。本文将详细介绍备份密集数据库备份的必要性&#xff0c;以及…