双向链表是最常用的数据类型了,这里就不过多解释,直接上图与代码。
一共有4个文件,代码如下:
1、list.h
#ifndef LIST_H_
#define LIST_H_
typedef void * List; // 链表对象
typedef int (*pForEach)(void *); // 链表遍历回调函数指针
#define list_create(type) (list_init(sizeof(type))) // 链表初始化宏替换
// 链表初始化
List list_init(int);
// 链表头插
int list_push_front(List,void *);
// 链表尾插
int list_push_back(List,void *);
// 链表删除
int list_erase(List,int);
// 链表大小
int list_size(List);
// 链表某节点数据
void *list_get(List,int);
// 链表遍历
void list_for_each(List,pForEach);
// 链表清空
int list_clear(List);
// 链表销毁
void list_destroy(List);
#endif
2、list.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
// 链表节点
typedef struct _stu_node
{
void *data; // 本节点数据
struct _stu_node *prev; // 上节点指针
struct _stu_node *next; // 下节点指针
}stu_node;
// 链表
typedef struct _stu_list
{
stu_node head; // 头节点
int size; // 链表元素大小
int dataSize; // 节点数据所占内存大小
}stu_list;
// 链表初始化
List list_init(int dataSize)
{
stu_list *pList = malloc(sizeof(stu_list));
if(pList == NULL){return NULL;}
pList->head.data = NULL;
pList->head.prev = &pList->head;
pList->head.next = &pList->head;
pList->size = 0;
pList->dataSize = dataSize;
return pList;
}
// 链表插入
static int list_push(stu_list *me,void *data,stu_node *node1,stu_node *node2)
{
if(me == NULL || data == NULL){return -1;}
// 新节点创建
stu_node *newNode = malloc(sizeof(stu_node));
if(newNode == NULL){return -1;}
newNode->data = malloc(me->dataSize); // 这里新复制对象而非存储原对象指针
if(newNode->data == NULL){return -1;}
memcpy(newNode->data,data,me->dataSize);
newNode->prev = NULL;
newNode->next = NULL;
// 新节点插入
newNode->prev = node1;
newNode->next = node2;
newNode->prev->next = newNode;
newNode->next->prev = newNode;
// 链表大小
me->size++;
return 0;
}
// 链表头插
int list_push_front(List me,void *data)
{
if(me == NULL || data == NULL){return -1;}
stu_list *pList = me;
return list_push(pList,data,&pList->head,pList->head.next);
}
// 链表尾插
int list_push_back(List me,void *data)
{
if(me == NULL || data == NULL){return -1;}
stu_list *pList = me;
return list_push(pList,data,pList->head.prev,&pList->head);
}
// 链表删除
int list_erase(List me,int pos)
{
if(me == NULL){return -1;}
stu_list *pList = me;
if(pos >= pList->size){return -1;}
// 移动到指定位置
stu_node *curNode = &pList->head;
int i;
for(i=0;i<=pos;i++){curNode = curNode->next;}
// 开始删除
curNode->prev->next = curNode->next;
curNode->next->prev = curNode->prev;
free(curNode->data);
free(curNode);
// 链表大小
pList->size--;
return 0;
}
// 链表遍历
void list_for_each(List me,pForEach pFun)
{
if(me == NULL){return;}
stu_list *pList = me;
if(pList->size == 0){return;}
stu_node *curNode = pList->head.next;
int i;
for(i=0;i<=pList->size;i++)
{
if(pFun(curNode->data) == -1){break;}
curNode = curNode->next;
}
}
// 链表某节点数据
void *list_get(List me,int pos)
{
if(me == NULL){return NULL;}
stu_list *pList = me;
if(pos >= pList->size){return NULL;}
stu_node *curNode = &pList->head;
int i;
for(i=0;i<=pos;i++){curNode = curNode->next;}
return curNode->data;
}
// 链表大小
int list_size(List me)
{
if(me == NULL){return -1;}
stu_list *pList = me;
return pList->size;
}
// 链表清空
int list_clear(List me)
{
if(me == NULL){return -1;}
stu_list *pList = me;
if(pList->size == 0){return 0;}
stu_node *curNode = pList->head.next;
stu_node *tmpNode = NULL;
int i;
for(i=0;i<pList->size;i++)
{
tmpNode = curNode->next;
free(curNode->data);
free(curNode);
curNode = tmpNode;
}
// 还原链表状态
pList->head.prev = &pList->head;
pList->head.next = &pList->head;
pList->size = 0;
return 0;
}
// 链表销毁
void list_destroy(List me)
{
if(me == NULL){return;}
list_clear(me);
stu_list *pList = me;
free(pList);
}
3、main.c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
// 测试数据
typedef struct _stu_person
{
int id;
char name[32];
int age;
}stu_person;
// 链表遍历回调函数
int myPrint(void *data)
{
if(data == NULL){return -1;}
stu_person *p = data;
printf("%d %s %d\n",p->id,p->name,p->age);
return 0;
}
int main()
{
// 变量定义
int i;
stu_person person;
// 链表创建
List list = list_create(stu_person); // 宏替换后变为 List list = list_init(sizeof(stu_person));
if(list == NULL){exit(1);}
// 数据插入
for(i=0;i<5;i++)
{
person.id = i;
sprintf(person.name,"stu%d",i);
person.age = random() % 100;
list_push_back(list,&person); // 尾插
//list_push_front(list,&person); // 头插
}
// 数据遍历
for(i=0;i<list_size(list);i++)
{
stu_person *p = list_get(list,i);
printf("%d %s %d\n",p->id,p->name,p->age);
}
// 删除数据
printf("===删除第3条数据===\n");
list_erase(list,2);
// 数据遍历
list_for_each(list,myPrint);
// 链表清空
list_clear(list);
// 链表销毁
list_destroy(list);
exit(0);
}
4、makefile
CC = gcc
OBJ = main
OBJS = main.o list.o
$(OBJ):$(OBJS)
$(CC) $^ -o $@
%.o:%.c
$(CC) -c -Wall $^ -o $@
.PHONY:clean
clean:
rm -rf *.o $(OBJ)
运行结果如下: