C语言作为面向过程的语言,想写出灵活的结构与封装需要很高技巧。
但由于C语言的高效,几乎所有操作系统和面向对象语言的最底层实现都使用了C语言。即,使用C完成面向对象的封装。
这次通过整理与仿写Linux的双向链表让我体会到了一些C语言封装的核心技巧。
这个双向链表的巧妙之处在于1)利用宏将“函数”入参扩展出了“结构类型”; 2)利用纯地址偏移获取结构体指针;
下面是具体实现:链表的实现由于是宏定义,都在libList.h中。
测试程序testList.c与testList.h。
libList.h
#ifndef _LIBLIST_H
#define _LIBLIST_H
#ifdef __cplusplus
extern "C" {
#endif
/* 链表头节点定义 */
typedef struct tagstList_Head {
struct tagstList_Head *next, *prev;
}stList_Head;
/* 初始化双向链表 */
#define list_init(head) do \
{ \
(head)->next = (head)->prev = (head); \
} while(0)
/* 在指定元素(where)之后插入新元素(item) */
#define list_add(item, towhere) do \
{ \
(item)->next = (towhere)->next; \
(item)->prev = (towhere); \
(towhere)->next = (item); \
(item)->next->prev = (item); \
} while(0)
/* 在指定元素(where)之前插入新元素(item) */
#define list_add_before(item, towhere) \
list_add(item,(towhere)->prev)
/* 删除某个元素 */
#define list_remove(item) do \
{ \
(item)->prev->next = (item)->next; \
(item)->next->prev = (item)->prev; \
} while(0)
/* 正向遍历链表中所有元素 */
#define list_for_each_item(item, head)\
for ((item) = (head)->next; (item) != (head); (item) = (item)->next)
/* 反向遍历链表中所有元素 */
#define list_for_each_item_rev(item, head) \
for ((item) = (head)->prev; (item) != (head); (item) = (item)->prev)
/* 根据本节点(item)获取节点所在结构体(type)的地址 */
/* 节点item地址(member的地址) - 该链表元素member在结构体中的偏移 */
#define list_entry(item, type, member) \
((type *)((char *)item - (char *)(&((type *)0)->member)))
/* 判断链表是否为空 */
#define list_is_empty(head) \
((head)->next == (head))
/* 获取指定位置上一元素 */
#define list_prev_item(item)\
((head)->prev)
#ifdef __cplusplus
}
#endif
#endif
testList.h
#ifndef _TESTLIST_H
#define _TESTLIST_H
#ifdef __cplusplus
extern "C" {
#endif
#include "libList.h"
typedef struct tagInteger
{
int idx;
stList_Head stListHead;
}stInteger;
extern void testlist();
#ifdef __cplusplus
}
#endif
#endif
这种双向链表的特点在于:在需要链表管理的结构中定义一个链表节点变量,然后所有的操作都是针对这个链表节点进行的。在需要获取该结构时使用list_entry即可。
#ifdef __cplusplus
extern "C" {
#endif
#include <malloc.h>
#include <stddef.h>
#include "stdafx.h"
#include "testList.h"
void testlist()
{
int i = 0;
stList_Head stListHead;
stList_Head *pstListHead= NULL;
stInteger *pstInteger = NULL;
stList_Head *pstTmpList = NULL;
/* 链表初始化 */
pstListHead = &stListHead;
list_init(pstListHead);
/* 生成节点并挂入链表 */
for(i = 0; i < 32; i++)
{
pstInteger = (stInteger *)malloc(sizeof(stInteger));
if (NULL == pstInteger)
{
printf("pstInteger malloc fail!");
return;
}
pstInteger->idx = 100 - i;
/* 挂到头结点之前 */
list_add_before(&pstInteger->stListHead, pstListHead);
/* 挂到头结点之后 */
//list_add(&pstInteger->stListHead, pstListHead);
}
/* 正向遍历链表中每一个元素并输出 */
list_for_each_item(pstTmpList, pstListHead)
{
if(NULL != pstTmpList)
{
pstInteger = list_entry(pstTmpList, stInteger, stListHead);
printf("%d ",pstInteger->idx);
}
}
return;
}
#ifdef __cplusplus
{
#endif