本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.
本文使用C语言实现了双向链表,可以存储任意类型的数据。指针类型使用了标准库中类型intptr_t,可以兼容32位和64位系统。
本文将链表的结构封装在list.c中,对外提供了一些操作API,并可以传入任意结构的数据到链表中。这样提高了代码的抽象性和复用性,并简化了操作。
链表涉及到指针操作,操作不当易导致内存泄漏。本文虽做了一定程度的封装,但应用到项目中,还是要阅读源码理解后再安全的使用。
list.h
/**
* Copyright (c) 2019-2019 jdh99 All rights reserved.
* @file list.h
* @brief 双向链表头文件
* @verbatim
* Change Logs:
* Date Author Notes
* 2019-03-04 jdh 新建
* @endverbatim
*/
#ifndef LIST_H_
#define LIST_H_
#include "stdio.h"
#include "stdbool.h"
#include "stdint.h"
#include "stdlib.h"
#include "string.h"
/**
* @brief 创建双向链表
* @return 双向链表索引
*/
intptr_t list_create(void);
/**
* @brief 是否为空
* @param index:双向链表索引
* @return true:空.false:非空
*/
bool list_is_empty(intptr_t index);
/**
* @brief 获得节点中的数据
* @note 注意本函数获取的是数据.传入的指针必须要先开辟好足够空间
* @param node_index: 节点索引
* @param data_ptr: 读取的数据指针.读取的数据会复制到其中
* @return 0: 无此节点或者无数据.其他: 数据字节数
*/
uint32_t list_get_data(intptr_t node_index, uint8_t *data_ptr);
/**
* @brief 获得节点中的数据指针
* @note 注意本函数获取的是数据指针
* @param node_index: 节点索引
* @param data_ptr: 二维指针.存储数据指针
* @return 0: 无此节点或者无数据.其他: 数据字节数
*/
uint32_t list_get_data_ptr(intptr_t node_index, uint8_t **data_ptr);
/**
* @brief 删除链表
* @param index:双向链表索引的地址
*/
void list_drop(intptr_t *index_ptr);
/**
* @brief 删除所有节点
* @param index:双向链表索引
*/
void list_clear(intptr_t index);
/**
* @brief 删除节点
* @param index:双向链表索引
* @param node_index: 节点索引
*/
void list_remove(intptr_t index, intptr_t node_index);
/**
* @brief 创建空节点
* @param data_size:数据大小.单位:字节
* @return 节点索引
*/
intptr_t list_create_empty_node(uint32_t data_size);
/**
* @brief 创建节点
* @param data: 数据指针.data可以为NULL
* @param data_size:数据大小.单位:字节
* @return 节点索引
*/
intptr_t list_create_node(uint8_t *data, uint32_t data_size);
/**
* @brief 获取下个节点索引
* @param node_index: 节点索引
* @return NULL: 无下个节点.其他:下个节点索引
*/
intptr_t list_get_next_node(intptr_t node_index);
/**
* @brief 获取上个节点索引
* @param node_index: 节点索引
* @return NULL: 无上个节点.其他:上个节点索引
*/
intptr_t list_get_last_node(intptr_t node_index);
/**
* @brief 在某个节点前插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
* @param node_index: 节点索引
*/
void list_insert_before_node(intptr_t index, intptr_t new_node_index, intptr_t node_index);
/**
* @brief 在某个节点后插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
* @param node_index: 节点索引
*/
void list_insert_after_node(intptr_t index, intptr_t new_node_index, intptr_t node_index);
/**
* @brief 获取首节点
* @param index:双向链表索引
* @return NULL: 获取失败.其他:首节点索引
*/
intptr_t list_get_header(intptr_t index);
/**
* @brief 获取尾节点
* @param index:双向链表索引
* @return NULL: 获取失败.其他:尾节点索引
*/
intptr_t list_get_tail(intptr_t index);
/**
* @brief 断开链接
* @param index:双向链表索引
* @param node_index: 节点索引
*/
void list_break_link(intptr_t index, intptr_t node_index);
/**
* @brief 在列表最前面插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
*/
void list_prepend(intptr_t index, intptr_t new_node_index);
/**
* @brief 在列表最后面插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
*/
void list_append(intptr_t index, intptr_t new_node_index);
#endif
list.c
/**
* Copyright (c) 2019-2019 jdh99 All rights reserved.
* @file list.h
* @brief 双向链表主文件
* @author jdh
* @verbatim
* Change Logs:
* Date Author Notes
* 2019-03-04 jdh 新建
* @endverbatim
*/
#include "list.h"
#pragma pack(1)
/**
* @brief 链表结构
*/
typedef struct _Node
{
uint8_t *data_ptr;
uint32_t data_size;
struct _Node *next;
struct _Node *last;
} Node, *NodePtr;
/**
* @brief 链表根节点
*/
typedef struct _Root
{
NodePtr header;
NodePtr tail;
// 占据字节数指的是用户数据占据的字节数
// 实际占据字节数还包括链表本身大小占据的字节数
uint32_t occupied_bytes;
uint32_t real_occupied_bytes;
} Root, *RootPtr;
#pragma pack()
/**
* @brief 删除节点后清除根节点中占据的空间
* @param root_ptr:链表根节点指针
* @param node_ptr: 节点指针
* @return false: 清除异常.true:清除成功
*/
static bool delete_node_occupied(RootPtr root_ptr, NodePtr node_ptr);
static void insert_fisrt_node(RootPtr root_ptr, NodePtr new_node_ptr);
/**
* @brief 创建双向链表
* @return 双向链表索引
*/
intptr_t list_create(void)
{
RootPtr root_ptr = (RootPtr)malloc(sizeof(Root));
root_ptr->header = NULL;
root_ptr->tail = NULL;
root_ptr->occupied_bytes = 0;
root_ptr->real_occupied_bytes = sizeof(Root);
return (intptr_t)root_ptr;
}
/**
* @brief 是否为空
* @param index:双向链表索引
* @return true:空.false:非空
*/
bool list_is_empty(intptr_t index)
{
RootPtr root_ptr = (RootPtr)index;
if (root_ptr == NULL)
{
return true;
}
return (root_ptr->header == NULL);
}
/**
* @brief 获得节点中的数据
* @note 注意本函数获取的是数据.传入的指针必须要先开辟好足够空间
* @param node_index: 节点索引
* @param data_ptr: 读取的数据指针.读取的数据会复制到其中
* @return 0: 无此节点或者无数据.其他: 数据字节数
*/
uint32_t list_get_data(intptr_t node_index, uint8_t *data_ptr)
{
if (node_index == (intptr_t)NULL)
{
return 0;
}
NodePtr node_ptr = (NodePtr)node_index;
memcpy(data_ptr, node_ptr->data_ptr, node_ptr->data_size);
return node_ptr->data_size;
}
/**
* @brief 获得节点中的数据指针
* @note 注意本函数获取的是数据指针
* @param node_index: 节点索引
* @param data_ptr: 二维指针.存储数据指针
* @return 0: 无此节点或者无数据.其他: 数据字节数
*/
uint32_t list_get_data_ptr(intptr_t node_index, uint8_t **data_ptr)
{
if (node_index == (intptr_t)NULL)
{
return 0;
}
NodePtr node_ptr = (NodePtr)node_index;
*data_ptr = node_ptr->data_ptr;
return node_ptr->data_size;
}
/**
* @brief 删除链表
* @param index:双向链表索引的地址
*/
void list_drop(intptr_t *index_ptr)
{
if (index_ptr == NULL || *index_ptr == (intptr_t)NULL)
{
return;
}
list_clear(*index_ptr);
free((void *)(*index_ptr));
*index_ptr = (intptr_t)NULL;
}
/**
* @brief 删除所有节点
* @param index:双向链表索引
*/
void list_clear(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
while (1)
{
if (root_ptr->header == NULL)
{
break;
}
list_remove(index, (intptr_t)(root_ptr->header));
}
}
/**
* @brief 删除节点
* @param index:双向链表索引
* @param node_index: 节点索引
*/
void list_remove(intptr_t index, intptr_t node_index)
{
if (index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
if (node_ptr->last != NULL)
{
node_ptr->last->next = node_ptr->next;
}
else
{
// 删除的是首节点
root_ptr->header = node_ptr->next;
}
if (node_ptr->next != NULL)
{
node_ptr->next->last = node_ptr->last;
}
else
{
// 删除的是尾节点
root_ptr->tail = node_ptr->last;
}
free(node_ptr->data_ptr);
node_ptr->data_ptr = NULL;
if (delete_node_occupied(root_ptr, node_ptr) == false)
{
// todo
}
free(node_ptr);
node_ptr = NULL;
}
/**
* @brief 删除节点后清除根节点中占据的空间
* @param root_ptr:链表根节点指针
* @param node_ptr: 节点指针
* @return false: 清除异常.true:清除成功
*/
static bool delete_node_occupied(RootPtr root_ptr, NodePtr node_ptr)
{
if (root_ptr->occupied_bytes < node_ptr->data_size)
{
return false;
}
root_ptr->occupied_bytes -= node_ptr->data_size;
root_ptr->real_occupied_bytes -= node_ptr->data_size + sizeof(Node);
return true;
}
/**
* @brief 创建空节点
* @param data_size:数据大小.单位:字节
* @return 节点索引
*/
intptr_t list_create_empty_node(uint32_t data_size)
{
NodePtr node_ptr = (NodePtr)malloc(sizeof(Node));
node_ptr->last = NULL;
node_ptr->next = NULL;
node_ptr->data_ptr = NULL;
node_ptr->data_size = 0;
if (data_size > 0)
{
node_ptr->data_ptr = malloc(data_size);
node_ptr->data_size = data_size;
memset(node_ptr->data_ptr, 0, node_ptr->data_size);
}
else
{
node_ptr->data_ptr = NULL;
node_ptr->data_size = 0;
}
return (intptr_t)node_ptr;
}
/**
* @brief 创建节点
* @param data: 数据指针.data可以为NULL
* @param data_size:数据大小.单位:字节
* @return 节点索引
*/
intptr_t list_create_node(uint8_t *data, uint32_t data_size)
{
NodePtr node_ptr = (NodePtr)(list_create_empty_node(data_size));
if (data != NULL && data_size > 0)
{
memcpy(node_ptr->data_ptr, data, data_size);
}
return (intptr_t)node_ptr;
}
/**
* @brief 获取下个节点索引
* @param node_index: 节点索引
* @return NULL: 无下个节点.其他:下个节点索引
*/
intptr_t list_get_next_node(intptr_t node_index)
{
if (node_index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
NodePtr node_ptr = (NodePtr)node_index;
return (intptr_t)(node_ptr->next);
}
/**
* @brief 获取上个节点索引
* @param node_index: 节点索引
* @return NULL: 无上个节点.其他:上个节点索引
*/
intptr_t list_get_last_node(intptr_t node_index)
{
if (node_index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
NodePtr node_ptr = (NodePtr)node_index;
return (intptr_t)(node_ptr->last);
}
/**
* @brief 在某个节点前插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
* @param node_index: 节点索引
*/
void list_insert_before_node(intptr_t index, intptr_t new_node_index, intptr_t node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
NodePtr new_node_ptr = (NodePtr)new_node_index;
new_node_ptr->last = node_ptr->last;
new_node_ptr->next = node_ptr;
if (node_ptr->last != NULL)
{
node_ptr->last->next = new_node_ptr;
}
else
{
// 首节点
root_ptr->header = (NodePtr)new_node_index;
}
node_ptr->last = new_node_ptr;
root_ptr->occupied_bytes += new_node_ptr->data_size;
root_ptr->real_occupied_bytes += new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 在某个节点后插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
* @param node_index: 节点索引
*/
void list_insert_after_node(intptr_t index, intptr_t new_node_index, intptr_t node_index)
{
if (new_node_index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
NodePtr new_node_ptr = (NodePtr)new_node_index;
new_node_ptr->last = node_ptr;
new_node_ptr->next = node_ptr->next;
if (node_ptr->next != NULL)
{
node_ptr->next->last = new_node_ptr;
}
else
{
// 尾节点
root_ptr->tail = (NodePtr)new_node_index;
}
node_ptr->next = new_node_ptr;
root_ptr->occupied_bytes += new_node_ptr->data_size;
root_ptr->real_occupied_bytes += new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 获取首节点
* @param index:双向链表索引
* @return NULL: 获取失败.其他:首节点索引
*/
intptr_t list_get_header(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
RootPtr root_ptr = (RootPtr)index;
return (intptr_t)(root_ptr->header);
}
/**
* @brief 获取尾节点
* @param index:双向链表索引
* @return NULL: 获取失败.其他:尾节点索引
*/
intptr_t list_get_tail(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
RootPtr root_ptr = (RootPtr)index;
return (intptr_t)(root_ptr->tail);
}
/**
* @brief 断开链接
* @param index:双向链表索引
* @param node_index: 节点索引
*/
void list_break_link(intptr_t index, intptr_t node_index)
{
if (index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
if (node_ptr->last != NULL)
{
node_ptr->last->next = node_ptr->next;
}
if (node_ptr->next != NULL)
{
node_ptr->next->last = node_ptr->last;
}
node_ptr->last = NULL;
node_ptr->next = NULL;
if (delete_node_occupied(root_ptr, node_ptr) == false)
{
// todo
}
}
/**
* @brief 在列表最前面插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
*/
void list_prepend(intptr_t index, intptr_t new_node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
if (root_ptr->header == NULL)
{
insert_fisrt_node(root_ptr, (NodePtr)new_node_index);
return;
}
list_insert_before_node(index, new_node_index, (intptr_t)(root_ptr->header));
root_ptr->header = (NodePtr)new_node_index;
}
static void insert_fisrt_node(RootPtr root_ptr, NodePtr new_node_ptr)
{
root_ptr->header = new_node_ptr;
root_ptr->tail = new_node_ptr;
root_ptr->occupied_bytes = new_node_ptr->data_size;
root_ptr->real_occupied_bytes = new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 在列表最后面插入
* @param index:双向链表索引
* @param new_node_index: 新节点索引
*/
void list_append(intptr_t index, intptr_t new_node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
if (root_ptr->header == NULL)
{
insert_fisrt_node(root_ptr, (NodePtr)new_node_index);
return;
}
list_insert_after_node(index, new_node_index, (intptr_t)(root_ptr->tail));
root_ptr->tail = (NodePtr)new_node_index;
}
测试代码:
#include "list.h"
static void test_case0(void);
static void test_case1(void);
static void test_case2(void);
static void test_case3(void);
static void test_case4(void);
static void test_case5(void);
int main()
{
test_case0();
test_case1();
test_case2();
test_case3();
test_case4();
test_case5();
getchar();
return 0;
}
static void test_case0(void)
{
printf("-------------------->case0:尾部插入数组测试开始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
printf("尾部插入数据:\n");
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
printf("%d\t", buffer[j]);
}
printf("\n");
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
printf("遍历读取第一个节点并删除:\n");
while (1)
{
if (list_is_empty(list) == true)
{
break;
}
intptr_t node_index = list_get_header(list);
uint32_t bytes = list_get_data(node_index, buffer);
list_remove(list, node_index);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
}
printf("-------------------->case0:测试结束\n");
}
static void test_case1(void)
{
printf("-------------------->case1:首部插入结构体测试开始\n");
struct Test1
{
int a;
int b;
};
struct Test1 test1;
intptr_t list = list_create();
printf("首部插入数据:\n");
for (uint8_t i = 0; i < 5; i++)
{
test1.a = i;
test1.b = i;
printf("a = %d b = %d\n", test1.a, test1.b);
intptr_t node = list_create_node((uint8_t *)&test1, 10);
list_prepend(list, node);
}
printf("遍历读取第一个节点并删除:\n");
while (1)
{
if (list_is_empty(list) == true)
{
break;
}
intptr_t node_index = list_get_header(list);
list_get_data(node_index, (uint8_t *)(&test1));
list_remove(list, node_index);
printf("a = %d b = %d\n", test1.a, test1.b);
}
printf("-------------------->case1:测试结束\n");
}
static void test_case2(void)
{
printf("-------------------->case2:遍历队列测试开始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
printf("插入数据:\n");
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
printf("%d\t", buffer[j]);
}
printf("\n");
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
printf("从链表首部开始遍历:\n");
intptr_t node_index = list_get_header(list);
while (1)
{
if (node_index == (intptr_t)NULL)
{
break;
}
uint32_t bytes = list_get_data(node_index, buffer);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
node_index = list_get_next_node(node_index);
}
printf("从链表尾部开始遍历:\n");
node_index = list_get_tail(list);
while (1)
{
if (node_index == (intptr_t)NULL)
{
break;
}
uint32_t bytes = list_get_data(node_index, buffer);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
node_index = list_get_last_node(node_index);
}
printf("-------------------->case2:测试结束\n");
}
static void test_case3(void)
{
printf("-------------------->case3:1000000次写入然后清除列表测试开始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
uint32_t num = 1000000;
while (num--)
{
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
}
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
list_clear(list);
if (list_is_empty(list) == false)
{
printf("测试失败.检测到列表非空!\n");
break;
}
}
printf("-------------------->case3:测试结束\n");
}
static void test_case4(void)
{
printf("-------------------->case4:1000000次创建然后删除列表测试开始\n");
uint8_t buffer[10] = {0};
uint32_t num = 1000000;
while (num--)
{
intptr_t list = list_create();
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
}
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
list_drop(&list);
if (list_is_empty(list) == false)
{
printf("测试失败.检测到列表非空!\n");
break;
}
}
printf("-------------------->case4:测试结束\n");
}
static void test_case5(void)
{
printf("-------------------->case5:节点数据指针操作\n");
intptr_t list = list_create();
intptr_t node_index = list_create_empty_node(10);
list_append(list, node_index);
uint8_t *data_ptr = NULL;
uint32_t data_size = list_get_data_ptr(node_index, &data_ptr);
printf("节点大小:data_size = %d\n", data_size);
printf("写入数据:\n");
for (uint8_t i = 0; i < data_size; i++)
{
data_ptr[i] = i;
printf("%d\t", data_ptr[i]);
}
printf("\n");
intptr_t node_index_get = list_get_tail(list);
uint8_t buffer[10] = {0};
uint32_t data_size_get = list_get_data(node_index_get, buffer);
printf("读取数据:\n");
for (uint8_t i = 0; i < data_size_get; i++)
{
printf("%d\t", buffer[i]);
}
printf("\n");
printf("-------------------->case5:测试结束\n");
}
测试输出:
-------------------->case0:尾部插入数组测试开始
尾部插入数据:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
遍历读取第一个节点并删除:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
-------------------->case0:测试结束
-------------------->case1:首部插入结构体测试开始
首部插入数据:
a = 0 b = 0
a = 1 b = 1
a = 2 b = 2
a = 3 b = 3
a = 4 b = 4
遍历读取第一个节点并删除:
a = 4 b = 4
a = 3 b = 3
a = 2 b = 2
a = 1 b = 1
a = 0 b = 0
-------------------->case1:测试结束
-------------------->case2:遍历队列测试开始
插入数据:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
从链表首部开始遍历:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
从链表尾部开始遍历:
4 4 4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
-------------------->case2:测试结束
-------------------->case3:1000000次写入然后清除列表测试开始
-------------------->case3:测试结束
-------------------->case4:1000000次创建然后删除列表测试开始
-------------------->case4:测试结束
-------------------->case5:节点数据指针操作
节点大小:data_size = 10
写入数据:
0 1 2 3 4 5 6 7 8 9
读取数据:
0 1 2 3 4 5 6 7 8 9
-------------------->case5:测试结束