序言:在学了c++之后,突然间想能不能用c来模拟实现一个泛型链表。
直接进入正题。
众所周知,c++在定义了一个模板后,如果在使用了一个类型的模板函数或类,编译器在编译时会自动创建该类型的函数或类。
而我们如果想使用c实现泛型,可以用宏来定义一个模板(幸好c的代码缩进没有严格要求),如果我们要用不同类型的函数,我们可以手动去定义。
上代码"list.h"
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <malloc.h>
#define LLNODE(type) struct type##_llnode{struct type##_llnode* next;type e;};
#define LLIST(type) struct type##_llist{struct type##_llnode* list;int num;};
#define LLFIND(type) bool type##_llfind(struct type##_llist* L,int pos,type * e);
#define LLADD(type) bool type##_lladd(struct type##_llist* L,int pos,type e);
#define LLDELETE(type) bool type##_lldelete(struct type##_llist* L,int pos);
#define LLCHANGE(type) bool type##_llchange(struct type##_llist* L,int pos,type e);
#define INCLUDE_LL(type) LLNODE(type) LLIST(type) LLFIND(type) LLADD(type) LLDELETE(type) LLCHANGE(type)
#define POS_LEGAL_LORO(pos,b,e) ((pos>=b)&&(pos<(int)(e)))
#define POS_LEGAL_LORC(pos,b,e) ((pos>=b)&&(pos<=(int)(e)))
#define LLFIND_FUN(type) bool type##_llfind(struct type##_llist* L,int pos,type * e)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
if(e == NULL)return false;\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos;i++)node = node->next;\
*e = node->e;\
return true;\
}
#define LLADD_FUN(type) bool type##_lladd(struct type##_llist* L,int pos,type e)\
{\
if(!POS_LEGAL_LORC(pos,0,L->num))return false;\
struct type##_llnode* n = (struct type##_llnode *)malloc(sizeof(struct type##_llnode));\
if(n == NULL)return false;\
n->next = NULL;\
n->e = e;\
if (pos == 0) \
{\
n->next = L->list;\
L->list = n;\
L->num++;\
return true;\
}\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos-1;i++)node = node->next;\
n->next = node->next;\
node->next = n;\
L->num++;\
return true;\
}
#define LLDELETE_FUN(type) bool type##_lldelete(struct type##_llist* L,int pos)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
struct type##_llnode* n = L->list;\
if (pos == 0) \
{\
L->list = n->next;\
free(n);\
L->num--;\
return true;\
}\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos-1;i++)node = node->next;\
n = node->next;\
node->next = n->next;\
free(n);\
L->num--;\
return true;\
}
#define LLCHANGE_FUN(type) bool type##_llchange(struct type##_llist* L,int pos,type e)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos;i++)node = node->next;\
node->e = e;\
return true;\
}
#define SOURCE_LL(type) LLFIND_FUN(type) LLADD_FUN(type) LLDELETE_FUN(type) LLCHANGE_FUN(type)
#define LLcreate(type,name) struct type##_llist name = {NULL,0}
#define LLlen(L) ((L)->num)
#define LLfind(type) type##_llfind
#define LLadd(type) type##_lladd
#define LLdelete(type) type##_lldelete
#define LLchange(type) type##_llchange
#define LLpush_back(type,l,e) type##_lladd(l,(l)->num,e)
#define LLpop_up(type,l) type##_lldelete(l,((l)->num - 1))
INCLUDE_LL(int)
INCLUDE_LL(float)
#define LLNODE(type) struct type##_llnode{struct type##_llnode* next;type e;};
#define LLIST(type) struct type##_llist{struct type##_llnode* list;int num;};
#define LLFIND(type) bool type##_llfind(struct type##_llist* L,int pos,type * e);
#define LLADD(type) bool type##_lladd(struct type##_llist* L,int pos,type e);
#define LLDELETE(type) bool type##_lldelete(struct type##_llist* L,int pos);
#define LLCHANGE(type) bool type##_llchange(struct type##_llist* L,int pos,type e);
#define INCLUDE_LL(type) LLNODE(type) LLIST(type) LLFIND(type) LLADD(type) LLDELETE(type) LLCHANGE(type)
↑这是链表类以及它的方法的模板,INCLUDE_LL(type)是声明,好让我们能够去使用这些操作,不过我在下面定义了更便于去使用的方法
#define POS_LEGAL_LORO(pos,b,e) ((pos>=b)&&(pos<(int)(e)))
#define POS_LEGAL_LORC(pos,b,e) ((pos>=b)&&(pos<=(int)(e)))
↑这是确定pos是否合法的宏,POS_LEGAL_LORO左开右开区间 POS_LEGAL_LORC左开右闭区间
#define LLFIND_FUN(type) bool type##_llfind(struct type##_llist* L,int pos,type * e)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
if(e == NULL)return false;\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos;i++)node = node->next;\
*e = node->e;\
return true;\
}
#define LLADD_FUN(type) bool type##_lladd(struct type##_llist* L,int pos,type e)\
{\
if(!POS_LEGAL_LORC(pos,0,L->num))return false;\
struct type##_llnode* n = (struct type##_llnode *)malloc(sizeof(struct type##_llnode));\
if(n == NULL)return false;\
n->next = NULL;\
n->e = e;\
if (pos == 0) \
{\
n->next = L->list;\
L->list = n;\
L->num++;\
return true;\
}\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos-1;i++)node = node->next;\
n->next = node->next;\
node->next = n;\
L->num++;\
return true;\
}
#define LLDELETE_FUN(type) bool type##_lldelete(struct type##_llist* L,int pos)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
struct type##_llnode* n = L->list;\
if (pos == 0) \
{\
L->list = n->next;\
free(n);\
L->num--;\
return true;\
}\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos-1;i++)node = node->next;\
n = node->next;\
node->next = n->next;\
free(n);\
L->num--;\
return true;\
}
#define LLCHANGE_FUN(type) bool type##_llchange(struct type##_llist* L,int pos,type e)\
{\
if(!POS_LEGAL_LORO(pos,0,L->num))return false;\
struct type##_llnode* node = L->list;\
for(int i = 0;i<pos;i++)node = node->next;\
node->e = e;\
return true;\
}
#define SOURCE_LL(type) LLFIND_FUN(type) LLADD_FUN(type) LLDELETE_FUN(type) LLCHANGE_FUN(type)
↑方法实例模板,SOURCE_LL(type)用于创建方法实例
#define LLcreate(type,name) struct type##_llist name = {NULL,0}
#define LLlen(L) ((L)->num)
#define LLfind(type) type##_llfind
#define LLadd(type) type##_lladd
#define LLdelete(type) type##_lldelete
#define LLchange(type) type##_llchange
#define LLpush_back(type,l,e) type##_lladd(l,(l)->num,e)
#define LLpop_up(type,l) type##_lldelete(l,((l)->num - 1))
↑便于我们使用的一些方法,包括创建表头,获取链表长度,增删查改,以及尾删和尾插。使用起来类似于函数重载,函数名固定,但是,参数类型不同,不过我们要显性的指明类型,如:我要往一个float元素的链表插入一个元素,我们必须有这样写,
LLadd(float)(&list,0,1.32);
这样写LLadd有点像返回函数指针的函数😁
使用方法:
在一个头文件加入INCLUDE_LL(type),实例化声明,type是你要使用的类型,如:
在“list.h"声明
INCLUDE_LL(int)
INCLUDE_LL(float)
同时在一个源文件加入SOURCE_LL(type),实例化方法,如:
#include "llist.h"
SOURCE_LL(int)
SOURCE_LL(float)
这样就可以使用了
#include "llist.h"
#include <stdio.h>
int main()
{
LLcreate(float, L);
LLpush_back(float, &L, 3.32);
LLpush_back(float, &L, 2.34);
LLadd(float)(&L, 0, 1.32);
LLadd(float)(&L, 0, 324.456);
for (float i = 0,ip = 0; i < LLlen(&L); i++)
{
LLfind(float)(&L, i,&ip);
printf("%f\n", ip);
}
putchar('\n');
LLpop_up(float, &L);
for (float i = 0, ip = 0; i < LLlen(&L); i++)
{
LLfind(float)(&L, i, &ip);
printf("%f\n", ip);
}
return 0;
}
窗口打印:

定义链表元素类型type时可以是任何类型,前提是用宏模板手动去定义了这套类和方法
这样写的好处是,提高了代码的复用率,用起来更加舒服,但是带来的后果就是调试困难(不过幸好vs有宏展开功能,在使用时无法大脑模拟调试是可以用宏展开来调试)
不过这只是一种c泛型编程的写法,还可以用void*来实现,不过缺点还是,需要在使用时时刻记住你使用的是什么类型
本文介绍了如何使用C语言通过宏定义实现泛型链表,包括节点结构、链表结构以及查找、添加、删除和更改操作。虽然提高了代码复用性,但调试难度增加,可通过宏展开辅助调试。
1301

被折叠的 条评论
为什么被折叠?



