两种C链表接口构造方式

        记录下C语言中数据结构链表的2种方式,其中内核的表达方式可谓是经典。

一、数据和指针域混合

        数据域和指针域定义在同一个结构体定义中,C语言中没有模板,意味着一个代码工程有多少数据结构就需要多少对链表的.c和.h,事实上这样会导致很多的冗余代码。

头文件 list.h
#include<stdio.h>typedef struct node
{DATATYPE data;    //数据域struct node *next;    //指针域
}node;typedef struct list
{node *first;    //指向头节点node *last;     //指向尾节点(该成员可以要可以不要)int size;
};//初始化单链表
void init_list(list *list);
//尾部新增数据node
void push_back(list *list, DATATYPE buf);
//头部新增数据node
void push_front(list *list, DATATYPE buf);
//打印链表数据结构
void show_list(list *list);
//尾部删除数据node
void pop_back(list *list, DATATYPE buf);
//头部删除数据node
void pop_front(list *list, DATATYPE buf);
//获取链表数据深度
int length(list *list);
//清空链表数据结构
void clear(list *list);
//销毁链表
void destroy(list *list);
接口文件 list.c
#include "list.h"
#include <stdio.h>//初始化链表,head结点不计入size,且数据域无值
void init_list(list *list)
{list->first = list->last = (node *)malloc(sizeof(node));assert(NULL != list->first);//为head节点赋NULL初始值list->first->next = NULL;list->size = 0;
}//尾插,存入新结点数据
void push_back(list *list, DATATYPE buf)
{//创建尾插节点node *s = (node *)malloc(sizeof(node));assert(NULL != s);s->data = buf;    //根据数据结构DATATYPE做相应处理s->next = NULL;//将尾插节点连接到链表尾部list->last-next = s;//更改管理节点中last的指向;list->last = s;//更改有效节点的个数list->size++;
}//头插,存入新节点数据
void push_front(list *list, DATATYPE buf)
{node *s = (node *)malloc(sizeof(node));assert(NULL != s);s-data = buf;s-next = list->first->next;list->first->next = s;//如果是链表的第一个有效节点,需要改变尾指针的指向if(list->size == 0){list->last = s;}//更改有效节点的个数list->size++;
}//尾删,取出结点数据
void pop_back(list *list, DATATYPE* buf)
{//链表中是否还有结点if(list->size == 0)return ;//寻找倒数第二个结点node *p = list->first;while(p->next != list->last)p = p->next;//取出尾部节点数据buf = p->next->data;//删除尾部结点free(p->next);//更改尾指针的指向list->last = p;//更改现尾结点的指针域list->last->next = NULL;list->size --;
}//头删,取出结点数据
void pop_front(list *list, DATATYPE* buf)
{//链表中是否有元素?if(list->size == 0)return;//临时保存要删除结点的地址Node *p = list->first->next;//删除该结点与链表的连接list->first->next = p->next;//取出头节点数据buf = p;//删除结点free(p);//该链表是否只有一个有效结点if(list->size == 1){//更改尾指针的指向list->last = list->first;}//更改有效结点个数list->size--;
}

二、数据和指针域分离

        数据域与指针域分离是Linux内核的写法,非常有特点,抽象了链表中共性的指针操作,让使用者只需要关注自己的数据域。

头文件list_common.h

只构造小结构体 list 相关的宏或则接口,该部分是共用的只需要一份;

//2024-04-01 实践记录
#include<stdio.h>
#include<stdlib.h>//遍历链表,typeof内建函数在vc下无法编译,不确定什么原因
//返回:结构体成员在内存中的偏移量
#define offsetof(type, memb) (unsigned long)(&((type *)0)->memb)
//返回:通过结构体变量的一个成员的地址找到这个结构体变量的首地址;ptr,type,member分别代表指针、类型、成员
#define container_of(ptr, type, member) ({          \const typeof(((type *)0)->member)*__mptr = (ptr);    \(type *)((char *)__mptr - offsetof(type, member)); })//以pos遍历以head为头的链表
#define list_entry(ptr, type, member)    \((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_entry(pos, head, member)				\for (pos = container_of((head)->next, typeof(*pos), member);		\&pos->member != (head);					\pos = container_of(pos->member.next, typeof(*pos), member))//定义一个双向链表
struct list_head {struct list_head *next, *prev;
};
//初始化链表
static inline void
INIT_LIST_HEAD(struct list_head *list)
{list->next = list->prev = list;
}//链表操作
static inline void
__list_add(struct list_head *entry,struct list_head *prev, struct list_head *next)
{next->prev = entry;entry->next = next;entry->prev = prev;prev->next = entry;
}//在head之后插入节点
static inline void
list_add(struct list_head *entry, struct list_head *head)
{__list_add(entry, head, head->next);
}//在尾部添加节点
static inline void
list_add_tail(struct list_head *entry, struct list_head *head)
{__list_add(entry, head->prev, head);
}//删除链表结点
static inline void __list_del(struct list_head* prev, struct list_head* next)
{next->prev = prev;prev->next = next;
}static inline void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = (void *) 0;entry->prev = (void *) 0;
}
头文件xxx_list.h

构造大结构体的定义,与具体的数据结构相关,需要定义多份;

#include<stdio.h>
#include<stdlib.h>
#include"list_common.h"#define DATATYPE inttypedef struct node{int data;struct list_head list;
}node, *pnode;pnode init_list(void);
int push_front(pnode head, DATATYPE buf);
void list_front(pnode node);
int pop_front(pnode head, DATATYPE buf);
int show(pnode head);
接口文件xxx_list.c

构造大结构体的接口,与具体的数据结构相关,需要定义多份; 

//2024-04-02 实践记录
#include<stdio.h>
#include<stdlib.h>
#include"list_common.h"//借助内核链表宏定义完成双向循环链表空表初始化
pnode init_list(void)
{pnode head = (pnode)malloc(sizeof(node));if(NULL == head){perror("malloc failed.\n");return NULL;}INIT_LIST_HEAD(&head->list);return head;
}int push_front(pnode head, DATATYPE buf)
{pnode newnode = (pnode)malloc(sizeof(node));if(NULL == newnode){perror("malloc failed.\n");return -1;}newnode->data = buf;//根据实际数据结构操作 list_add(&(newnode->list), &(head->list));return 0;
}//删除结点
void list_front(pnode node)
{list_del(&(node->list));free(node);
}int pop_front(pnode head, DATATYPE buf)
{node p;struct list_head *pos = (&(head->list))->next;p = list_entry(pos, node, list);buf = p->data;    //根据实际数据结构赋值list_front(p);
}//结点打印
int show(struct node *head)
{node *pos;list_for_each_entry(pos, &(head->list), list){printf("data is:", pos->data);//与大结构的具体定义相关        }return 0;
}

测试代码:

//2024-04-02 实践记录#include<stdio.h>
#include<stdlib.h>
#include"node_list.h"int main()
{int i, n;int num;pnode head = init_list();printf("please input num you want create node: \n");scanf("%d", &n);for(i = 1; i <= n; i ++){push_front(head, i);}show(head);pop_front(p, &num);printf(“get node.data: %d.\n”, num);show(head);return 0;
}

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

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

相关文章

【MATLAB第102期】基于MATLAB的BRT增强回归树多输入单输出回归预测模型

【MATLAB第102期】基于MATLAB的BRT增强回归树多输入单输出回归预测模型 BRT&#xff0c;即Boosted Regression Trees&#xff08;增强回归树&#xff09;&#xff0c;是一种用于回归问题的集成学习方法。它结合了多个决策树模型&#xff0c;通过逐步改进的方式来提高整体模型的…

GDAL源码剖析(九)之GDAL体系架构

GDAL源码剖析&#xff08;九&#xff09;之GDAL体系架构_gdal 源码-CSDN博客 在GDAL库中包含栅格数据的读写&#xff0c;矢量数据的读写&#xff0c;以及栅格和矢量数据的相关算法。下面主要对GDAL中栅格数据和矢量数据的体系架构做一个简单的说明。本人英文很烂&#xff0c;有…

力扣经典150题第一题:合并两个有序数组

目录 合并两个有序数组问题详解与解决方法1. 介绍2. 问题描述3. 解题思路4. 算法实现5. 复杂度分析6. 测试和验证7. 扩展如何处理特殊情况和边界条件&#xff1f;如何处理数组中可能存在的重复元素&#xff1f;如何优化算法以减少内存使用或提高执行效率&#xff1f; 8. 总结9.…

Linux 多线程与线程控制(程序均有详细注释)

多线程与线程控制 线程的基本概念线程的特点页表多线程 线程控制线程的创建线程传参线程id资源回收---线程等待线程id和LWP 封装一个线程库线程互斥和线程同步线程互斥基本原理线程安全VS线程不安全锁的诞生可重入VS线程安全 死锁死锁的定义 线程同步条件变量接口 生成消费者模…

使用Python获取红某书笔记详情并批量无水印下载

根据红某手最新版 请求接口必须要携带x-s x-s-c x-t,而调用官方接口又必须携带cookie,缺一不可,获取笔记详情可以通过爬取网页的形式获取&#xff0c;虽然也是无水印&#xff0c;但是一些详情信息只能获取大概&#xff0c;并不是详细的数值&#xff0c;因此既不想自己破解x-s x…

Java中的类加载器及其作用详解

Java中的类加载器&#xff08;ClassLoader&#xff09;是Java运行时环境&#xff08;JRE&#xff09;的一部分&#xff0c;负责在运行时动态地加载Java类到Java虚拟机&#xff08;JVM&#xff09;中。Java的类加载机制是Java核心的一部分&#xff0c;它提供了高度的灵活性和可扩…

面了金山和 OPPO 的 NLP 算法岗,还热乎的面经分享给大家

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

清明时节雨纷纷,AI达人用Bedrock(第一季)

今天是清明小长假第一天&#xff0c;没有外出踏青&#xff0c;在家体验Amazon Bedrock的强大能力。Amazon Bedrock是专门为创新者量身打造的平台&#xff0c;它提供了构建生成式人工智能应用程序所需的一切。 这次我主要尝试的是通过 Amazon Bedrock 里的 Stability AI SDXL 1…

C++理解std::move和转发(std::forward)

理解 std::move 标准库move函数是使用右值引用的模板的一个很好的例子。 幸运的是&#xff0c;我们不必理解move所使用的模板机制也可以直接使用它。 但是&#xff0c;研究move是如何工作的可以帮助我们巩固对模板的理解和使用。 我们注意到&#xff0c;虽然不能直接将一个…

提升工作效率:B端工作台设计基础详解

随着互联网和信息技术的快速发展&#xff0c;越来越多的企业开始以数字化、智能化的方式管理和运营自己的业务。B端工作台设计作为企业应用的重要组成部分&#xff0c;越来越受到重视。本文将从三个方面对B端工作台设计进行全面分析。让我们看看。 1. B端工作台设计原则 B端工…

攻防世界 wife_wife

在这个 JavaScript 示例中&#xff0c;有两个对象&#xff1a;baseUser 和 user。 baseUser 对象定义如下&#xff1a; baseUser { a: 1 } 这个对象有一个属性 a&#xff0c;其值为 1&#xff0c;没有显式指定原型对象&#xff0c;因此它将默认继承 Object.prototype。 …

不堪大用的pow

【题目描述】 输出100&#xff5e;999中的所有水仙花数。若3位数ABC满足&#xff0c;则称其为水仙花 数。例如&#xff0c;所以153是水仙花数。 【题目来源】 刘汝佳《算法竞赛入门经典 第2版》习题2-1 水仙花数&#xff08;daffodil&#xff09; 题目很简单&#xff0c;…

AI大模型在未来如何与体育运动结合

AI大模型与体育运动的结合预示着未来体育领域将迎来革命性的变化。这种结合不仅能提升运动员的训练和比赛表现&#xff0c;还能改善观众的观赛体验&#xff0c;优化体育管理和运营。以下是几个预期中AI大模型将如何与体育运动结合的方向&#xff1a; 1. 运动员表现分析与提升 …

配置vite配置文件更改项目端口、使用@别名

一、配置vite配置文件更改项目端口 vite官方文档地址&#xff1a;开发服务器选项 | Vite 官方中文文档 (vitejs.dev) 使用&#xff1a; 二、使用别名 1. 安装 types/node types/node 包允许您在TypeScript项目中使用Node.js的核心模块和API&#xff0c;并提供了对它们的类型…

C语言经典例题(17) --- 最小公倍数、单词倒置、你是天才吗?、完美成绩、判断整数的奇偶性

1.最小公倍数 正整数A和正整数B的最小公倍数是指能被A和B整除的最小的正整数&#xff0c;设计一个算法&#xff0c;求输入A和B的最小公倍数。 输入描述&#xff1a;输入两个正整数A和B。 输出描述&#xff1a;输出A和B的最小公倍数。 输入&#xff1a;5 7 输出&#xff1a…

4. python练习题4-水仙花数

4. python练习题4-水仙花数 【目录】 文章目录 4. python练习题4-水仙花数1. 目标任务2. 水仙花数的特点3. 如何判断一个数是否是水仙花数&#xff1f;4. 打印3位水仙花数5. 判断一个数是不是水仙花数6. 列表推导式6. 列表推导式判断一个数是不是水仙花数 【正文】 1. 目标任务…

picGo图床搭建gitee和smms(建议使用)

picGoGitee 这个需要下载gitee插件, 因为官方频繁的检索文件类型, 有时候也会失效 如果没有特殊要求平时存个学习的要看图中文字的重要的图片建议就是smms, 免费也够用! 图片存本地不方便, 各种APP中来回传还会失帧损失画质, 所以你值得往下看 picGosmms 建议使用这个, sm…

代码随想录-算法训练营day01【二分查找、移除元素、插入元素】

专栏笔记&#xff1a;https://blog.csdn.net/weixin_44949135/category_10335122.html 第一章 数组part01今日任务 数组理论基础&#xff0c;704. 二分查找&#xff0c;27. 移除元素 详细布置数组理论基础 文章链接&#xff1a;https://programmercarl.com/%E6%95%B0%E7%BB%…

递归与树的深度优先搜索:探索它们之间的关系

文章目录 递归与树的深度优先搜索&#xff1a;探索它们之间的关系递归的基本概念树的深度优先搜索递归与树的深度优先搜索的关系反转链表合并两个有序链表 总结 递归与树的深度优先搜索&#xff1a;探索它们之间的关系 递归是一种强大而优雅的编程技术,它允许我们通过将问题分…

每日面经分享(python part1)

Python中的深拷贝和浅拷贝的区别是什么&#xff1f; a. 浅拷贝创建一个新的对象&#xff0c;但其中的可变元素仍然共享引用。只有对象的第一层被复制&#xff0c;而更深层次的嵌套对象仍然是引用。更改其中一个对象的属性会影响到其他对象。 b. 深拷贝创建一个完全独立的新对象…