单链表的完整C语言实现(含测试例子)

这篇博客详细介绍了如何使用C语言实现链表的初始化、插入、删除、查找等基本操作,并通过实例展示了如何创建和管理链表。文章还提供了一个用于打印链表节点的辅助函数,以及一个具体的打印示例,展示了如何存储和打印Person结构体数据。最后,通过一个完整的main函数演示了链表的完整生命周期,包括创建、操作和释放链表内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//完全成功运行 

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

//链表结点结构体 
typedef struct LINKNODE{
	void* data;   //无类型指针,能够指向任何数据。如果只是想做int类型数据的插入,可以只写一句int data 
	struct LINKNODE* next;
}LinkNode;


//链表结构体 
typedef struct LINKLIST{
	LinkNode* head;
	int size;
}LinkList;

//【附属】打印函数指针 
typedef void(*PRINTLINKNODE)(void*); 


/*
以下,LinkList是个指针类型,所以才会在之后不断地出现LinkList* list 
*/ 


//初始化链表
LinkList* Init_LinkList(){
	LinkList* list = (LinkList*)malloc (sizeof(LinkList));
	list->size = 0;
	//头结点是不保存数据信息 ————一般建议整个带头结点的链表,更加方便,省去一些繁琐的担心 
	list->head = (LinkNode*)malloc (sizeof(LinkNode));
	list->head->data = NULL;
	list->head->next = NULL;
	
	return list;
};

//指定位置插入 
void Insert_LinkList(LinkList* list, int pos, void* data){  //三个参数分别是:链表,指定位置,插入结点的数据 
	if (list == NULL){
		return;
	}
	if (data == NULL){
		return;
	}
	//一个友好的处理——对于插入位置pos越界的话 
	if (pos < 0 || pos > list->size) {
		pos = list->size ;
	} 
	
	//创建新的结点
	LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
	newnode->data = data;
	newnode->next = NULL;
	
	//找结点
	//辅助指针变量
	LinkNode* pCurrent = list->head;
	int i;
	for (i = 0; i<pos; i++){
		pCurrent = pCurrent->next; 
		
		
	}
	//新结点进入链表 
	 newnode->next = pCurrent->next;
	 pCurrent->next = newnode; 
	
	list->size++;
	
};

//删除指定位置的值
void RemoveByPos_LinkList(LinkList* list, int pos){
	if (list == NULL){
		return;
	}
	
	if (pos <0 || pos >=list->size ){
		return;
	}
	
	//查找删除结点的前一个结点 
	LinkNode* pCurrent = list->head;
	int j ;
	for (j=0; j<pos; j++ ){
		pCurrent = pCurrent->next; 
	} 
	//先缓存删除的结点 
	LinkNode* pDel = pCurrent->next;
	pCurrent->next = pDel->next;
	
	//释放删除结点的内存 
	free(pDel);
	
	list->size--; 
	
}; 

//获得链表的长度 
int Size_LinkList(LinkList* list){
	return list->size; 
};

//查找 
int Find_LinkList(LinkList* list, void* data){
	if (list == NULL){
		return -1;
	}
	if (data == NULL){
		return -1;
	}
	//遍历查找 
	LinkNode* pCurrent = list->head->next ;
	int i = 0;
	while (pCurrent != NULL) {
		if (pCurrent->data = data) {
			break;
		}
		i++; 
		pCurrent = pCurrent->next;   //指针要向后移动 
	}
	return i; 
	
};


//返回第一个结点 
void* Front_LinkList(LinkList* list){
	return list->head->next ;
	
};


//打印链表节点【需一个附属的打印指针函数,在上面】 
void Print_LinkList(LinkList* list, PRINTLINKNODE print){
	if (list == NULL){
		return;
	}
	//辅助指针变量
	LinkNode* pCurrent = list->head ->next;
	while (pCurrent != NULL){
		print (pCurrent->data);
		pCurrent = pCurrent->next ;
	}
	
	
};




//释放链表内存 
void FreeSpace_LinkList(LinkList* list){
	if (list == NULL){
		return;
	}
	//辅助指针变量 
	LinkNode* pCurrent = list->head ;
	while (pCurrent != NULL){
		//缓存下一个结点 
		LinkNode* pNext = pCurrent->next;
		free(pCurrent);
		pCurrent = pNext;	
	} 
	// 释放链表内存 
	free(list); 
};



typedef struct PERSON{
	char name[64];
	int age; 
	int score;
}Person; 


//打印函数
void MyPrint(void* data){
	Person* p = (Person*)data;
	printf("Name:%s Age:%d Score:%d\n",p->name ,p->age ,p->score );
	
} 
 
int main(void){
	//创建链表
	LinkList* list = Init_LinkList();
	 
	//创建数据
	Person p1 = {"aaa",30,100};
	Person p2 = {"bbb",20,200};
	Person p3 = {"ccc",40,300};
	Person p4 = {"ddd",60,400};
	Person p5 = {"eee",80,500};
	
	
	//数据插入链表------头插法,倒着打印 
	Insert_LinkList(list, 0, &p1);
	Insert_LinkList(list, 0, &p2);	
	Insert_LinkList(list, 0, &p3);	
	Insert_LinkList(list, 0, &p4);	
	Insert_LinkList(list, 0, &p5);
	
	//打印 
	Print_LinkList(list, MyPrint);
	
	//销毁链表
	FreeSpace_LinkList(list); 
}

二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言下的单链表,可以增加,删除,查找,销毁节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值