通用双向链表的C语言实现

C语言通用双向链表的实现--设计的艺术 专用链表在这里是指该链表的实现和调用耦合在一起,只能被一个调用者使用,而不能单独在其他地方被重用。通用链表则相反,它具有通用性,可以在多处被重复使用。尽管通用链表相对专用链表来说有很多优越之处,不过草率地断定通用链表比专用链表好也是不公正的,因为它们都有自己的优点和适用范围。     专用链表的优点   性能更高。专用链表的实现和调用在一起,可以直接访问数据成员,省去了包装函数带来的性能开销,可以提高时间性能。专用链表无需实现完整的接口,只要满足自己的需要就行了,生成的代码更小,因此可以提高空间性能。 阅读详情

双向链表是一种基本的数据结构,在许多场合都会使用到。最近正在编写一个基于Linux的GUI系统,里边许多地方都要使用到双向链表,于是写了一个通用的双向链表。代码如下:

  1. #ifndef __STDLIST_H__
  2. #define __STDLIST_H__
  3. typedef struct tagSTDNODE       STDNODE, *LPSTDNODE;
  4. typedef struct tagSTDLIST       STDLIST, *LPSTDLIST;
  5. // 链表数据结构
  6. struct tagSTDNODE {
  7.     LPSTDNODE           lpPrev;
  8.     LPSTDNODE           lpNext;
  9. };
  10. // 链表节点结构
  11. struct tagSTDLIST {
  12.     LPSTDNODE           lpHead;
  13.     LPSTDNODE           lpTail;
  14. };
  15. // 用于链表扩展的宏
  16. #define NODE_INITIALIZER    ((STDNODE){ .lpPrev = NULL, .lpNext = NULL, })
  17. #define LIST_INITIALIZER    ((STDLIST){ .lpHead = NULL, .lpTail = NULL, })
  18. #define DECLARELISTNODE()  STDNODE __snStdNode
  19. #define INIT_LISTNODE()     __snStdNode = NODE_INITIALIZER
  20. // 初始化链表
  21. STATIC INLINE
  22. LPSTDLIST       TWINAPI     StdListInit(
  23.                             LPSTDLIST lpList
  24.                             ) {
  25.     lpList->lpHead  = NULL;
  26.     lpList->lpTail  = NULL;
  27.     return lpList;
  28. }
  29. // 获取链表头部节点
  30. STATIC INLINE
  31. LPSTDNODE       TWINAPI     StdListGetHeadNode(
  32.                             LPSTDLIST lpList
  33.                             ) {
  34.     return lpList->lpHead;
  35. }
  36. // 获取链表尾部节点
  37. STATIC INLINE
  38. LPSTDNODE       TWINAPI     StdListGetTailNode(
  39.                             LPSTDLIST lpList
  40.                             ) {
  41.     return lpList->lpTail;
  42. }
  43. // 获取给定节点的下一个节点
  44. STATIC INLINE
  45. LPSTDNODE       TWINAPI     StdListGetNextNode(
  46.                             LPSTDNODE lpNode
  47.                             ) {
  48.     return lpNode->lpNext;
  49. }
  50. // 获取给定节点的上一个节点
  51. STATIC INLINE
  52. LPSTDNODE       TWINAPI     StdListGetPrevNode(
  53.                             LPSTDNODE lpNode
  54.                             ) {
  55.     return lpNode->lpPrev;
  56. }
  57. // 在链表头部插入一个节点
  58. STATIC INLINE
  59. VOID            TWINAPI     StdListPushFront(
  60.                             LPSTDLIST lpList,
  61.                             LPSTDNODE lpNode
  62.                             ) {
  63.     lpNode->lpPrev  = NULL;
  64.     lpNode->lpNext  = lpList->lpHead;
  65.     if(lpList->lpHead)
  66.         lpList->lpHead->lpPrev  = lpNode;
  67.     else
  68.         lpList->lpTail  = lpNode;
  69.     lpList->lpHead  = lpNode;
  70. }
  71. // 在链表尾部插入一个节点
  72. STATIC INLINE
  73. VOID            TWINAPI     StdListPushBack(
  74.                             LPSTDLIST lpList,
  75.                             LPSTDNODE lpNode
  76.                             ) {
  77.     lpNode->lpNext  = NULL;
  78.     lpNode->lpPrev  = lpList->lpTail;
  79.     if(lpList->lpTail)
  80.         lpList->lpTail->lpNext  = lpNode;
  81.     else
  82.         lpList->lpHead  = lpNode;
  83.     lpList->lpTail  = lpNode;
  84. }
  85. // 在指定节点后插入一个节点
  86. STATIC INLINE
  87. VOID            TWINAPI     StdListInsert(
  88.                             LPSTDLIST lpList,
  89.                             LPSTDNODE lpAfter,
  90.                             LPSTDNODE lpNode
  91.                             ) {
  92.     if(lpAfter) {
  93.         if(lpAfter->lpNext)
  94.             lpAfter->lpNext->lpPrev = lpNode;
  95.         else
  96.             lpList->lpTail  = lpNode;
  97.         lpNode->lpPrev  = lpAfter;
  98.         lpNode->lpNext  = lpAfter->lpNext;
  99.         lpAfter->lpNext = lpNode;
  100.     } else {
  101.         StdListPushFront(lpList, lpNode);
  102.     }
  103. }
  104. // 从链表头部弹出一个节点
  105. STATIC INLINE
  106. LPSTDNODE       TWINAPI     StdListPopFront(
  107.                             LPSTDLIST lpList
  108.                             ) {
  109.     if(lpList->lpHead) {
  110.         LPSTDNODE lpNode    = lpList->lpHead;
  111.         if(lpList->lpHead->lpNext)
  112.             lpList->lpHead->lpNext->lpPrev  = NULL;
  113.         else
  114.             lpList->lpTail  = NULL;
  115.         lpList->lpHead  = lpList->lpHead->lpNext;
  116.         lpNode->lpPrev  = lpNode->lpNext    = NULL;
  117.         return lpNode;
  118.     } else {
  119.         return NULL;
  120.     }
  121. }
  122. // 从链表尾部弹出一个节点
  123. STATIC INLINE
  124. LPSTDNODE       TWINAPI     StdListPopBack(
  125.                             LPSTDLIST lpList
  126.                             ) {
  127.     if(lpList->lpTail) {
  128.         LPSTDNODE lpNode    = lpList->lpTail;
  129.         if(lpList->lpTail->lpPrev)
  130.             lpList->lpTail->lpPrev->lpNext  = NULL;
  131.         else
  132.             lpList->lpHead  = NULL;
  133.         lpList->lpTail  = lpList->lpTail->lpPrev;
  134.         lpNode->lpPrev  = lpNode->lpNext    = NULL;
  135.         return lpNode;
  136.     } else {
  137.         return NULL;
  138.     }
  139. }
  140. // 从链表中删除给定节点
  141. STATIC INLINE
  142. LPSTDNODE       TWINAPI     StdListRemove(
  143.                             LPSTDLIST lpList,
  144.                             LPSTDNODE lpNode
  145.                             ) {
  146.     if(lpNode->lpPrev)
  147.         lpNode->lpPrev->lpNext  = lpNode->lpNext;
  148.     else
  149.         lpList->lpHead  = lpNode->lpNext;
  150.     if(lpNode->lpNext)
  151.         lpNode->lpNext->lpPrev  = lpNode->lpPrev;
  152.     else
  153.         lpList->lpTail  = lpNode->lpPrev;
  154.     return lpNode;
  155. }
  156. // 检查链表是否为空
  157. STATIC INLINE
  158. BOOL            TWINAPI     StdListIsEmpty(
  159.                             LPSTDLIST lpList
  160.                             ) {
  161.     if(lpList->lpHead || lpList->lpTail)
  162.         return FALSE;
  163.     else
  164.         return TRUE;
  165. }
  166. // 获取链表中的节点数
  167. STATIC INLINE
  168. LONG            TWINAPI     StdListGetSize(
  169.                             LPSTDLIST lpList
  170.                             ) {
  171.     LONG lnSize = 0;
  172.     LPSTDNODE lpNode    = StdListGetHeadNode(lpList);
  173.     while(lpNode) {
  174.         ++ lnSize;
  175.         lpNode  = StdListGetNextNode(lpNode);
  176.     }
  177.     return lnSize;
  178. }
  179. // 合并两个链表
  180. STATIC INLINE
  181. LPSTDLIST       TWINAPI     StdListCombine(
  182.                             LPSTDLIST lpList1,
  183.                             LPSTDLIST lpList2
  184.                             ) {
  185.     if(!StdListIsEmpty(lpList2)) {
  186.         if(!StdListIsEmpty(lpList1)) {
  187.             lpList1->lpTail->lpNext = lpList2->lpHead;
  188.             lpList2->lpHead->lpPrev = lpList1->lpTail;
  189.             lpList1->lpTail = lpList2->lpTail;
  190.         } else {
  191.             lpList1->lpHead = lpList2->lpHead;
  192.             lpList1->lpTail = lpList2->lpTail;
  193.         }
  194.         lpList2->lpHead = lpList2->lpTail   = NULL;
  195.     }
  196.     return lpList1;
  197. }
  198. #endif//__STDLIST_H__

算法本身没有什么特别的地方,为什么说这是一个通用双向链表呢?奥妙就在那四个用于扩展的宏。利用这几个宏你可以将这个双向链表扩展到任意结构。

例如,我们要实现一个消息队列,那么我们可以这样定义消息节点:

  1. typedef struct tagMSGNODE {
  2.     DECLARELISTNODE();
  3.     HWND        hWnd;
  4.     UINT        uMsg;
  5.     WPARAM      wParam;
  6.     LPARAM      lParam;
  7. } MSGNODE, *LPMSGNODE;

而消息队列则可以直接用LISTNODE结构来表示:

  1. STDLIST     lstMsgQueue;

这样我们就可以用刚刚定义的函数来对这个消息队列进行操作了:

  1. // 向消息队列插入一个消息
  2. LPMSGNODE lpMsgNode = MemAlloc(SIZEOF(MSGNODE));
  3. lpMsgNode->INIT_LISTNODE();
  4. lpMsgNode->hWnd     = hWnd;
  5. lpMsgNode->uMsg     = WM_PAINT;
  6. lpMsgNode->wParam   = NULL;
  7. lpMsgNode->lParam   = NULL;
  8. StdListPushBack(&lstMsgQueue, (LPSTDNODE)lpMsgNode);
  9. // 从消息队列取出一个消息
  10. LPMSGNODE lpMsgNode = (LPMSGNODE)StdListPopFront(&lstMsgQueue);
  11. // 删除所有属于hWnd的消息
  12. LPMSGNODE lpTemp    = (LPMSGNODE)StdListGetHeadNode(&lstMsgQueue);
  13. while(lpTemp) {
  14.     LPMSGNODE lpNext    = (LPMSGNODE)StdListGetNextNode(&lstMsgQueue, (LPSTDNODE)lpTemp);
  15.     if(lpTemp->hWnd == hWnd) {
  16.         StdListRemove(&lstMsgQueue, (LPSTDNODE)lpTemp);
  17.                 MemFree(lpTemp);
  18.         }
  19.     lpTemp  = lpNext;
  20. }

是不是很方便呢^_^。

 

备注:

由于正在编写的GUI是参考Windows的体系结构,所以代码风格采用了微软的风格。

代码中的MemAlloc,MemFree函数以及各种数据结构均在其他地方定义。

 

 

数据结构 | 通用数据类型双向链表(C语言) 目录前言设计思路代码展示测试环节 前言 熟悉C++的STL(标准模板库)的人都知道list底层是一个双向链表,支持通用类型数据的存储,使用起来非常方便,但对于C语言开发者来说,并没有这么方便的工具,所以在这里记录一下自己实现的通用数据类型双向链表,提供了一些常见的API,并分享一些常见的代码规范。 设计思路 这里我们不便过多地讨论双向链表的实现过程,我们重点看如何解决下面几个问题 1. 如何解决通用类型的数据存储 2. 如何保证代码的严谨性,健壮性 3. 如何实现数据结构的高复用,低耦合 1.通过 阅读详情

相关推荐

C语言中如何使用通用双向链表

通用双向链表,指的是通过一个通用的数据结构,然后添加特定数据形成有具体用途的链表。这主要涉及到代码复用,在C++中实现非常方便,只需要定义一个双向链表的类,每一个具体业务只需要继承它,然后添加自己的数据成员即可。但有时候我们需要用C语言而不是C++,那么该如何实现这种层次关系呢?首先我们来看两个宏。 第一个是offsetof(可参考Linux内核代码include/linux/Stddef.h):

hzy_76的专栏 1156

C语言---双向链表(详解)---数据结构

双向链表所需要头文件 首先重定义类型名 意义我前几篇讲过几次了,这里就不在赘述了,(顺序表,单链表的开头都有说明) 然后我们需要一个结构体 结构体包含 : 存储数据的 a 指向一个节点的指针 next 指向上一个节点的指针 prve 双向链表实现的函数功能有 :1.申请动态内存空间 2.初始化 3.双向链表的头,尾的插入与删除,在合法的任意位置进行插入和删除 4.释放链表,5.打印链表 1.申请动态内存空间 因为进行插入和初始化需要用到此函数 所以我们要先实现此功能 1.先申.

烁岩的博客 7821

疲劳容器的定义_FKM规范针对非焊接构件疲劳强度评估实例介绍(下篇)

一、写在前面FKM Inside ANSYS软件,该软件是在FKM 规范的基础上,在ANSYS Workbench内开发的进行非焊接构件及焊缝静强度和疲劳强度评估的工具包。基于ANSYS计算模型的信息,在图形用户界面定义相关设置,利用有限元分析计算结果,自动嵌入FKM规范要求的静强度计算和疲劳强度计算流程和方法,自动将应力结果和相关物理量传递给工具包,无须手动预先选择评估的热点区域,可以...

weixin_35925298的博客 1885

双向链表(数据结构)(C语言)

双向链表的结构体定义、双向链表的初始化、关于无头单向非循环链表无需初始化函数,顺序表、带头双向循环链表需要的思考、双向链表在pos位置之前插入x、双向链表的打印 双链表删除pos位置的结点、双向链表的尾插、关于单链表的尾插需要用到二级指针,双向链表不需要用到二级指针的思考、双向链表的判空、双向链表的尾删、双向链表的头插、双向链表的头删、双向链表查找值为x的结点、双向链表的销毁、双向链表的修改、双向链表删除值为x的结点、双向链表计算结点总数(不计phead)、双向链表获取第

outdated_socks的博客 5406

波士顿咨询最新发布《2025年全球支付报告:未来不稳定》:到2029 年,支付收入将预计达到 2.4 万亿美元

波士顿咨询最新发布《2025年全球支付报告:未来不稳定》:到2029 年,支付收入将预计达到 2.4 万亿美元

face light~ 917

FSMC驱动LCD显示屏学习2

液晶显示器,简称 LCD(Liquid Crystal Display)相对于上一代 CRT 显示器 (阴极射线管显示器),LCD显示器具有功耗低、体积小、承载的信息量大及不伤眼的优点,因而它成为了现在的主流电子显示设备液晶是一种介于固体和液体之间的特殊物质,它是一种有机化合物,常态下呈液态,但是它的分 子排列却和固体晶体一样非常规则,因此取名液晶。

usjjjsj的博客 1695

数据结构初阶 --- 双向链表的C语言实现

带头双向循环链表

m0_71722065的博客 2924

手把手教你用FPGA实现SGMII接口:从IP核配置到板级调试全流程

本文详细介绍了如何在Xilinx 7系列FPGA上实现SGMII(Serial Gigabit Media Independent Interface)接口的全流程,包括IP核配置、约束编写和硬件调试。通过具体的硬件选型建议、软件工具链配置和PCB设计规范,帮助开发者快速构建稳定可靠的千兆通信链路,并提供了常见故障排查和性能优化技巧。

weixin_29194693的博客 296

【数据结构】—C语言实现双向链表(超详细!)

本文是作者对于双向链表以及循环链表的学习记录,也是对于链表学习画上的一个句号。

B1ue还在专注 5047

Python解析骑行fit文件:从数据缺失到完整记录

本文详细介绍了如何利用Python解析和处理骑行FIT文件,解决数据缺失问题。通过使用fitparse库读取二进制FIT文件,并结合Garmin FitCSVTool进行格式转换,指导读者一步步诊断数据完整性、设计心率修补方案,并最终生成完整的骑行记录文件。该方法为骑行爱好者提供了灵活的数据修复与自定义分析能力。

weixin_29062255的博客 456

银河麒麟高级服务器V11操作系统安装部署zabbix监控平台的实操保姆级教程

本文详细介绍了在《银河麒麟高级服务器V11上安部署Zabbix监控系统的全过程》涵盖了zabbix监控系统涉及到的所有软件安装、参数优化、服务启动验证及防火墙配置等一系列实操。其中关于安装过程报错undefined symbol: usmAES256CiscoPrivProtocol;ares_reinit等依赖库问题进行了详细记录与分析解决;还提供了Web前端中文显示修复方案和Admin密码修改方法。实现监控平台的100%部署上线,实现对业务系统的实时监控,保证业务的稳定运行。

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 531

双向链表---C语言实现

双向链表的结点由三个部分组成,一个部分用来保存当前节点存储的数据,一个部分用来保存下一个结点的地址,即next指针,还有一个部分用来保存前一个节点的地址,即prev指针。有了next指针和prev指针,我们才能实现双向和循环。//当前节点存储的数据//指向前一个节点的指针//指向下一个节点的指针}LTNode;

2302_80106957的博客 1592

华为OD机考双机位C卷 - 构成正方形的数量 (Java & Python& JS & C/C++ & GO )

华为OD机考双机位C卷 - 成正方形的数量 (OD上机考试双机位C卷)。提供在线OJ刷题。Java、C++、Python、GO语言、C语言、JavaScript题解,并提供完整思路讲解。

算法大师! 5938

实现通用的双向链表(c语言实现)

国庆8天在家阅读李先静大神的书籍,抽空把一些笔记记录下来。从实现一个通用的双向链表开始。 1. 构建链表节点   链表节点存值还是存值的地址(指针)?对于通用链表首先要做到能够存放任何数据类型的数据,首先可能会想到的是union,但是数据类型无穷无尽,不可能在union中全部表示。那么可行的办法有两种:   (1) 存值 typedef struct _DListNode DLis

echo_bright_的博客 8499

2025年下半年网络规划设计师综合知识真题及答案解析

1983年,ARPANet完成技术转型,对此后的 Internet发展全关重要,这一转型是指( ) A.电路交换转向分组交换 B.商业化转型 C.民用化转型 D.从 NCP 协议转向 TCP/IP 答案:D 解析:ARPANet 最初使用 NCP (Network Control Protocol)协议进行主机问通信。1983年1月1日,ARPANet 正式切换到TCP/IP 协议族,为后续Internet的发展奠定了统一协议基础。

鹿鸣天涯 927

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C语言通用双向链表的实现

作者:朱克锋  转载请注明出处:http://blog.youkuaiyun.com/linux_zkf/article/details/7493159 专用链表在这里是指该链表的实现和调用耦合在一起,只能被一个调用者使用,而不能单独在其他地方被重用。通用链表则相反,它具有通用性,可以在多处被重复使用。尽管通用链表相对专用链表来说有很多优越之处,不过草率地断定通用链表比专用链表好也是不公正的,因为它们都有

linux_zkf的专栏 2266

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

关于C语言通用双向链表实现的测试--回调函数的作用

把通用双向链表的测试拿出来写不仅仅是应因为和实现写在一起文章太长,而是因为我想在测试中体现一种思想:回调函数, 这里程序实现下列功能。   (1)找出链表中的最大值。   (2) 累加链表中所有整数。 这两个函数的实现与dlist_print的实现很类似,故采用回调函数的方式   不过这两个函数并没有太多实用价值,但是通过它们我们可以学习几点。   1,按传统的方法写出max之后,这个函数与dlist_print很类似,在写出sum_cb之后,那种感觉就更明显了。在这时就应该想办法把这些重复的代

ecorefeng的专栏 2780

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

c语言 链表_keil实现C语言的通用双向链表

1、前言链表是数据结构中非常重要的一个知识点,比较经典的就是C语言的链表,在学校也有这门课程,非计算机专业的朋友可能没学到这一章就毕业了(呵呵),在这里可以和我一起学习。因为个人偏好对链表的知识也没有面面俱到,全面的链表知识请多方查证学习。这篇文章主要介绍一个通用双向链表,之所以叫通用因为链表本身不包含特定应用层的设计,只是将多个节点连接起来组成一条链的结构,每个节点挂载的"内容"是应用层的设计。...

weixin_39831104的博客 685

C语言实现通用双向链表

双向链表的通用实现和测试用例

Orange_pa的博客 680

使用C语言来实现一个通用的双向链表。利用这个链表来统计一篇文章的不同词数,针对不同单词和同一单词的不同拼写形式进行排序

使用C语言来实现一个通用的双向链表。利用这个链表来统计一篇文章的不同词数,针对不同单词和同一单词的不同拼写形式进行排序 本文利用了C语言来实现了一个通用的双向链表,其中双向链表的实现借鉴了 实现通用的双向链表(c语言实现) 读李先静《系统程序员成长计划》内容总结 这几篇文章。向其表示感谢。 首先,我们要创建一个链表,然后对于链表中的一些操作(创建链表,插入数据,查找数据)等等基本的操作进行实现。在这里我主要是借鉴了之前的文章。然后我自己根据我的需要加入了一点方法。 我自己实现的方法主要有: 1、一个检测此

qq_45091038的博客 717

Generic double circular linked list - 通用双向循环链表C语言实现

在某种情况下,需要我们实现自己的双向循环链表。我采用C语言实现了这个链表。本文包括全部的源代码和测试文件。本文包括链表的全部操作。

恒 3278

C语言实现通用链表初步(四)----双向链表

在前面的文章中,我们讨论了如何实现通用类型的链表,方法是用void *类型的指针,指向数据。那么还有其他的方法吗(不考虑内核链表)? 答案是肯定的。用零长数组也可以实现。 struct node_info { struct node_info *next; struct node_info *prev; char data[0]; }; 这里的最后一个元素,是元素个...

weixin_30242907的博客 231

C语言通用双向链表

C语言通用双向链表接口封装。

m0_48995611的博客 453
下一篇: 内存池的C语言实现
EricLogic
博客等级 码龄18年 2粉丝 3原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值