编码基础一:侵入式链表

news2024/11/20 0:40:01

一、简介概述

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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网约车平台如何开发?需要多少钱?

随着共享经济的兴起&#xff0c;网约车行业迅速发展&#xff0c;并成为人们生活中不可或缺的一部分。为了满足市场需求和提供更好的服务&#xff0c;开发一款高质量的网约车源码平台至关重要。本文将深入探讨网约车源码平台的开发方案&#xff0c;从技术架构、安全性和用户体验…

Python数据分析实战-DataFrame按照某列指定的顺序排序(附源码和实现效果)

实现功能 dataframe按照某列指定的顺序排序 实现代码 import pandas as pd# 创建一个示例 DataFrame df pd.DataFrame({name: [Alice, Bob, Charlie, David, Eva],age: [25, 30, 35, 40, 45],gender: [F, M, M, M, F]})# 按照指定顺序排序 sort_order [Eva, David, Charli…

echarts图表共用一个 timeline(A表 timeline 同时控制B表)

先看效果: 再看代码(部分): let barOption = {baseOption: {height: 350,timeline: {x: center,//时间轴X轴方向位置设置y: bottom,//时间轴Y轴方向位置设置width: 80%,//宽度height: 50,//高度show:true,axisType: category,autoPlay: true,playInterval: 1000,data: [Em…

好书分享| 战斗细胞之从进化上了解免疫系统和肠道

小编最近仔细阅读了一本书&#xff0c;书名是《战斗细胞&#xff1a;人体免疫系统奇妙之旅》&#xff08;海南出版社2022年10月出版的&#xff09;&#xff0c;该书是德国科学家菲利普德特玛&#xff08;Philipp Dettmer&#xff09;撰写的一本书&#xff0c;作者是一名信息设计…

ARM开发,stm32mp157a-A7核PWM实验(驱动蜂鸣器,风扇,马达工作)

1.分析框图&#xff1b; 2.比较捕获寄存器&#xff08;产生PWM方波&#xff09;&#xff1b; 工作原理&#xff1a; 1、系统提供一个时钟源209MHZ&#xff0c;需要通过分频器进行分频&#xff0c;设置分频器值为209分频&#xff1b; 2、当定时器启动之后&#xff0c;自动重载…

Pygame编程(3)draw模块

draw模块 函数实例 函数 pygame.draw.rect 画一个矩形rect(surface, color, rect) -> Rectrect(surface, color, rect, width0, border_radius0, border_top_left_radius-1,border_top_right_radius-1, border_bottom_left_radius-1, border_bottom_right_radius-1) -> …

非常简单!用Java实现一个简单的向量数据库雏形。

概述 向量数据库是用来解决高维向量数据管理和查询的问题。它能够有效地存储、索引和查询大规模高维度向量数据&#xff0c;并提供高性能和高效的相似度搜索。传统的关系型数据库或文档数据库在处理高维向量数据时可能会遇到诸多问题。比如在高维空间中&#xff0c;数据点之间…

网红景区游乐设备普乐蛙5d动感影院体验馆设备组成内容

一个5D7D动感影院体验馆的全套设备组成通常包括以下几个方面&#xff1a; 电影播放设备&#xff1a;包括主控制器、电影播放器、电影储存设备等&#xff0c;用于播放5D电影。 影院座椅&#xff1a;一般采用特殊设计的动感座椅&#xff0c;具备震动、摇晃、抖动等功能&#xff0…

Mainline Linux 和 U-Boot编译

By Toradex胡珊逢 Toradex 自从 Linux BSP v6 开始在使用 32位处理器的 Arm 模块如 iMX6、iMX6ULL、iMX7 上提供 mainline/upstream kernel &#xff0c;部分 64位处理器模块如 Verdin iMX8M Mini/Plus 也提供实验性支持。文章将以季度发布版本 Linux BSP V6.3.0 为例介绍如何下…

为什么选择新风机?

现如今&#xff0c;新风机已经是很多场地的熟客了&#xff0c;那大家可能疑惑为什么选择新风机呢&#xff1f;那就让我揭晓答案吧&#xff01;新风机有很多益处&#xff0c;让我大致简述一下吧。 改善室内空气质量&#xff1a;新风机能够引入新鲜的外界空气&#xff0c;并排除室…

lab7 thread

文章目录 Uthread: switching between threadstaskhints思路上下文的恢复和保存thread_createthread_schedule Using threads思路 Barrier Uthread: switching between threads 在这个练习中&#xff0c;你将为一个用户级别线程系统设计上下文切换机制&#xff0c;并实现它。 …

算法leetcode|73. 矩阵置零(rust重拳出击)

文章目录 73. 矩阵置零&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 73. 矩阵置零&#xff1a; 给定一个 m x n 的矩…

邮件营销:高效的节日宣传方式

每个国家都有当地的传统节日&#xff0c;像是我国刚过去的端午节&#xff0c;即将迎来的中秋节、国庆节。我们除了会进行一些传统习俗外&#xff0c;各路商家还会趁这个机会开启促销活动。 对于公司来讲&#xff0c;抓住每一次营销活动都可能会带来更高的营销额&#xff0c;或…

ATA-7000系列高压放大器——应用场景介绍

ATA-7000系列是一款理想的可放大交、直流信号的高压放大器。单端输出20kVp-p&#xff08;10kVp&#xff09;高压&#xff0c;可以驱动高压型负载。电压增益数控可调&#xff0c;一键保存常用设置&#xff0c;为您提供了方便简洁的操作选择。 图&#xff1a;ATA-7000系列高压放大…

2021年长安杯电子数据取证比赛

VC挂载 一&二检材 长安杯-1 检材一 请计算检材一Apk的SHA256值 3fece1e93be4f422c8446b77b6863eb6a39f19d8fa71ff0250aac10f8bdde73a 长安杯-2 该APK的应用包名为 plus.H5B8E45D3 长安杯-3 APPID 该APK程序在封装服务商的应用唯一标识&#xff08;APPID&#xff09;为…

设计模式之抽象工厂

文章目录 一、介绍二、基本组件三、演示案例1. 定义抽象工厂2. 定义抽象产品3. 定义具体工厂4. 定义具体产品5. 代码演示6. 代码改造 四、总结 一、介绍 抽象工厂模式(Abstract Factory Pattern)属于创建型设计模式。用于解决比工厂方法设计模式更加复杂的问题。 复杂到哪里了…

U盘怎么加密?U盘加密方法有哪些?

U盘是我们生活和工作中最常用的移动储存设备&#xff0c;经常被用来存放各种重要数据&#xff0c;为了保证数据的安全&#xff0c;我们需要加密U盘。那么&#xff0c;U盘加密方法有哪些呢&#xff1f; U盘加密普通方法 如果你的U盘储存数据不多&#xff0c;并且对于加密的要求…

Orchestrator介绍三 命令行工具

Orchestrator-client orchestrator 支持两种方式通过命令行操作&#xff1a; 一种是 通过命令 orchestrator&#xff1a; 需要在服务器上安装 orchestrator&#xff0c;但是可以不作为服务启动。 需要配置orchestrator的文件&#xff0c;以便能够连接后端数据库 一种是通过…

Docker部署gogs仓库

Docker部署gogs Git仓库 拉取镜像 docker pull gogs/gogs查看本地镜像 docker images启动gogs仓库服务 创建数据挂在目录 我在/root目录下创建gogs挂在目录 mkdir gogs启动gogs docker run --namegogs -d -p 10022:22 -p 10880:3000 -v /root/gogs:/data gogs/gogs10022…

知识图谱Neo4j安装到实践全过程

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 在本次实战中&#xff0c;我们将一起完成知识图谱Neo4j安装到实践全过程&#xff0c;探索其中的关系和属性。知识图谱是一种以三元组形式存储的数据结构&#xff0c;由实体、关系和属性组成&#xff0c;能够帮助我们更好地…