LV060-单链表基础操作
一、单链表创建
1. 创建步骤

【注意】 这里用到了 malloc 内存申请函数,注意加上头文件 stdlib.h 。
2. 函数实现
/**
* @Function : slinkedlist_create
* @brief : 单链表创建
* @param arg : none
* @return : _slinkedlink 类型,NULL, 内存申请失败; H, 单链表地址
* @Description: 其实就是创建一个节点
*/
_slinkedlink slinkedlist_create(void)
{
/* 1.定义一个单链表结构体指针变量 */
_slinkedlink H;
/* 2.申请内存空间 */
H = (_slinkedlink)malloc(sizeof(_slinkednode));
/* 3.判断是否申请成功 */
if (H == NULL)
{
printf("[ERROR]Single linked list malloc failed!\n");
return H;
}
/* 4.初始化内存空间 */
H->data = 0;
H->next = NULL;
return H;
}二、单链表释放
1. 释放步骤

【注意】
(1) H 指向下一个节点的时候,为什么不用 H++ ?
如果说,节点是连续存放的,那可以像之前学习数组的时候一样进行 ++ 操作,但是这里是链表,各个节点的存储空间并非连续,所以 ++ 操作并没有任何意义,反而还可能会把后边的链表搞丢,链表的指针域就是指向下一个节点的,所以直接进行赋值才是正确的。即
H = H->next;(2)此种释放方式可以直接释放掉头节点。
(3)临时指针 p 和 头部指针 H 是指向同一个节点的,释放的时候一定要先移动头节点 H 到下一个节点,保证释放后的链表地址不丢失。

2. 函数实现
/**
* @Function : slinkedlist_free
* @brief : 单链表空间释放(释放整个单链表占用的内存空间)
* @param H : _slinkedlink 类型,表示要释放的单链表的地址
* @return : _slinkedlink 类型,NULL, 表示释放完成
* @Description:
*/
_slinkedlink slinkedlist_free(_slinkedlink H)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return NULL;
}
/* 2. 开始释放内存 */
p = H;
printf("Single linked list free:");
while (H != NULL)
{
p = H;
printf("%d ", p->data);
/* p 和 H 指向同一个地址空间,要先移动 H, 再释放,否则后边的节点地址就丢掉了 */
H = H->next;
free(p);
}
puts(""); /* 自带换行 */
return NULL;
}三、单链表数据显示
1. 数据显示步骤

2. 函数实现
/**
* @Function : slinkedlist_show
* @brief : 单链表数据显示
* @param H : _slinkedlink 类型,表示要释放的单链表的地址
* @return : int 类型,0, 显示成功;-1, 单链表不存在
* @Description:
*/
int slinkedlist_show(_slinkedlink H)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 遍历所有节点 */
p = H;
printf("Single linked list:");
while (p->next != NULL)
{
printf("%d ", p->next->data);
p = p->next;
}
puts(""); /* 自带换行 */
return 0;
}四、单链表节点查找
1. 节点查找步骤

【i、pos 和 p 的变化分析】
查找时,i、pos 和 p 的关系分析如下,假设我们现在一共就只有 2 个节点,如下图所示:

(1)我们现在查找 pos = -1 的节点,也就是头节点,毫无疑问,直接返回 H 即可;
(2)我们现在查找 pos = 0 的节点,也就是第 1 个节点:

(3)我们现在查找 pos = 1 的节点,也就是第 2 个节点:

(3)我们现在查找 pos = 5 的节点,也就是第 6 个节点:

用上一次分析的图吧,其实一共就俩节点,当第二次循环结束后,我们会发现若此时再进行一次循环的话,p 会指向 NULL,这已经是单链表结尾了,若是此时还没有循环到要找的节点位置,说明我们输入的参数已经超出了单链表的长度范围,就没有继续循环的意义了,所以此时就需要进行判断,提前退出循环。
经过上边的分析,我们得到了两个结束循环的条件,一个是 i < pos,一个是 p = NULL。
2. 函数实现
/**
* @Function : slinkedlist_node_get
* @brief : 单链表节点查找(按位置查找)
* @param H : _slinkedlink 类型,表示要查找的单链表的地址
* @param pos : int 类型,表示要查找的节点位置,pos 大于等于 -1
* @return : _slinkedlink 类型,查找成功则返回查找到的节点的地址,否则返回 NULL
* @Description:
*/
_slinkedlink slinkedlist_node_get(_slinkedlink H, int pos)
{
/* 定一个单链表结构体指针变量 */
_slinkedlink p;
int i = 0;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return NULL;
}
/* 2. 检查参数是否合法 */
if (pos == -1) /* pos = -1 表示查找的是头节点 */
{
return H;
}
/* 这里可以防止参数非法,而返回 H 的情况,可以防止删除节点函数误删 */
if (pos < -1)
{
printf("[ERROR]get pos is invalid\n");
return NULL;
}
/* 3. 开始遍历节点,查找所需节点 */
p = H;
i = -1; /* 一共需要循环 pos + 1 次 */
while (i < pos)
{
p = p->next;
if (p == NULL) /* 已经遍历到单链表尾部了 */
{
printf("[ERROR]get pos is invalid\n");
return NULL;
}
i++;
}
return p;
}五、单链表数据插入
1. 尾部插入
1.1 尾部插入步骤

具体的步骤如下:

1.2 函数实现
/**
* @Function : slinkedlist_tail_insert
* @brief : 单链表尾部插入数据
* @param H : _slinkedlink 类型,表示要插入节点的单链表的地址
* @param value: data_t 类型,表示要插入的数据
* @return : int 类型,0, 插入成功;-1, 插入失败
* @Description:
*/
int slinkedlist_tail_insert(_slinkedlink H, data_t value)
{
/* 定义两个个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 创建新节点并初始化 */
if ((p = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
{
printf("[ERROR]The new node malloc failed!\n");
return -1;
}
p->data = value;
p->next = NULL;
/* 3. 寻找尾节点 */
q = H;
while (q->next != NULL)
{
q = q->next;
}
/* 3. 插入新节点 */
q->next = p;
return 0;
}2. 指定位置插入
2.1 指定位置插入步骤

具体步骤如下:

【注意】
(1)在指定位置插入节点,关键在于我们要找到插入位置的 前一个节点,这里我们调用了上边实现的单链表节点查找函数。
(2)有两种情况是非法的,就是在 -1 位置插入,这意味着在头结点位置插入,这是非法的,所以位置 pos 需要大于等于 0;第二种情况是插入的位置已经大大于了整个链表的长度,这个时候我们是无法查找到前一个节点的,这也是一种非法插入。
(3)一定要注意,先建立新节点与原来链表插入位置处节点的联系,然后再将插入位置的前一个节点的指针域指向新节点,以保证链表完整,如下图所示:

2.2 函数实现
/**
* @Function : slinkedlist_insert
* @brief : 单链表在指定位置插入节点
* @param H : _slinkedlink 类型,表示要插入节点的单链表的地址
* @param value: data_t 类型,彪悍死要插入的数据
* @param pos : int 类型,表示要插入的节点位置,pos 大于等于 0
* @return : int 类型,0, 插入成功;-1, 插入失败
* @Description:
*/
int slinkedlist_insert(_slinkedlink H, data_t value, int pos)
{
/* 定义两个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 获取插入位置的前一个节点 */
p = slinkedlist_node_get(H, pos - 1);
if (p == NULL)
{
printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
return -1;
}
/* 3. 创建新节点并初始化 */
if ((q = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
{
printf("[ERROR]The new node malloc failed\n");
return -1;
}
q->data = value;
q->next = NULL;
/* 4. 插入新节点 */
q->next = p->next;
p->next = q;
return 0;
}六、单链表删除节点
1. 删除节点步骤

具体步骤:

【注意】
(1)确保删除位置的 pos 参数正确范围,需要在获取节点位置函数中加上小于 -1 时的处理情况,这是因为,原来的获取节点位置函数中当 pos 小于等于 -1 的时候都会返回头节点,这对于获取节点位置函数本身是没有问题的,但是对于删除函数就有问题了,给到一个负值或者 pos-1 小于了 -1 的时候, slinkedlist_node_get 函数都会返回头节点地址,这就意味着,删除函数将会删除链表中头节点后边的哪个节点。
2. 函数实现
/**
* @Function : slinkedlist_delete
* @brief : 删除指定位置节点
* @param H : _slinkedlink 类型,表示要删除节点的单链表的地址
* @param pos : int 类型,要删除的节点位置(pos 大于等于 0)
* @return : int 类型,0, 删除成功;-1, 删除失败
* @Description:
*/
int slinkedlist_delete(_slinkedlink H, int pos)
{
/* 定义两个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 获取删除位置的前一个节点 */
/* 注意在这里,不合法的 pos 范围会在获取节点函数中被处理 */
p = slinkedlist_node_get(H, pos - 1);
if (p == NULL)
{
printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
return -1;
}
if (p->next == NULL)
{
printf("delete pos is invalid!pos-1 = %d position is the last node!\n", pos - 1);
return -1;
}
/* 3. 更新节点 */
q = p->next;
p->next = q->next; /* p-> next = p-> next-> next; */
/* 4.释放删除的节点 */
printf("Single linked list node free:%d\n", q->data);
free(q);
q = NULL;
return 0;
}七、更新单链表节点数据
1. 更新节点数据步骤
具体步骤:

2. 函数实现
/**
* @Function : slinkedlist_node_update
* @brief : 更新指定位置的节点数据
* @param H : _slinkedlink 类型,表示要更新数据的单链表的地址
* @param value: data_t 类型,表示要更新的数据
* @param pos : int 类型,要更新的节点位置(pos 大于等于 0)
* @return : int 类型, 0, 更新成功;-1, 更新失败
* @Description:
*/
int slinkedlist_node_update(_slinkedlink H, data_t value, int pos)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("Single linked list is not existed!\n");
return -1;
}
if (pos < 0)
{
printf("update pos is invalid!\n");
return -1;
}
/* 2. 获取更新节点位置 */
p = slinkedlist_node_get(H, pos);
if (p == NULL)
{
printf("pos position is not existed!\n");
return -1;
}
/* 3. 更新节点 */
p->data = value;
return 0;
}八、求单链表长度
1. 求单链表长度步骤
具体步骤:

2. 函数实现
/**
* @Function : slinkedlist_length
* @brief : 获取单链表长度(不包含头节点)
* @param H : _slinkedlink 类型,表示要获取长度的单链表的地址
* @return : int 类型, 链表不存在则返回-1; 否则返回单链表长度
* @Description:
*/
int slinkedlist_length(_slinkedlink H)
{
/* 定义一个链表结构体指针变量 */
_slinkedlink p;
int len = 0;
/* 1.判断链表是否为存在, 不存在则直接返回 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2.遍历链表 */
p = H;
while (p->next != NULL)
{
len++;
p = p->next;
}
return (len);
}九、完整程序
1. slinkedlist.c
/** ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== =
* Copyright © hk. 2022-2025. All rights reserved.
* File name : slinkedlist.c
* Author : qidaink
* Date : 2022-10-21
* Version :
* Description:
* Others :
* Log :
* ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==
*/
#include <stdio.h>
#include <stdlib.h>
#include "slinkedlist.h"
/**
* @Function : slinkedlist_create
* @brief : 单链表创建
* @param arg : none
* @return : _slinkedlink 类型,NULL, 内存申请失败; H, 单链表地址
* @Description: 其实就是创建一个节点
*/
_slinkedlink slinkedlist_create(void)
{
/* 1.定义一个单链表结构体指针变量 */
_slinkedlink H;
/* 2.申请内存空间 */
H = (_slinkedlink)malloc(sizeof(_slinkednode));
/* 3.判断是否申请成功 */
if (H == NULL)
{
printf("[ERROR]Single linked list malloc failed!\n");
return H;
}
/* 4.初始化内存空间 */
H->data = 0;
H->next = NULL;
return H;
}
/**
* @Function : slinkedlist_free
* @brief : 单链表空间释放(释放整个单链表占用的内存空间)
* @param H : _slinkedlink 类型,表示要释放的单链表的地址
* @return : _slinkedlink 类型,NULL, 表示释放完成
* @Description:
*/
_slinkedlink slinkedlist_free(_slinkedlink H)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return NULL;
}
/* 2. 开始释放内存 */
p = H;
printf("Single linked list free:");
while (H != NULL)
{
p = H;
printf("%d ", p->data);
/* p 和 H 指向同一个地址空间,要先移动 H, 再释放,否则后边的节点地址就丢掉了 */
H = H->next;
free(p);
}
puts(""); /* 自带换行 */
return NULL;
}
/**
* @Function : slinkedlist_show
* @brief : 单链表数据显示
* @param H : _slinkedlink 类型,表示要释放的单链表的地址
* @return : int 类型,0, 显示成功;-1, 单链表不存在
* @Description:
*/
int slinkedlist_show(_slinkedlink H)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 遍历所有节点 */
p = H;
printf("Single linked list:");
while (p->next != NULL)
{
printf("%d ", p->next->data);
p = p->next;
}
puts(""); /* 自带换行 */
return 0;
}
/**
* @Function : slinkedlist_node_get
* @brief : 单链表节点查找(按位置查找)
* @param H : _slinkedlink 类型,表示要查找的单链表的地址
* @param pos : int 类型,表示要查找的节点位置,pos 大于等于 -1
* @return : _slinkedlink 类型,查找成功则返回查找到的节点的地址,否则返回 NULL
* @Description:
*/
_slinkedlink slinkedlist_node_get(_slinkedlink H, int pos)
{
/* 定一个单链表结构体指针变量 */
_slinkedlink p;
int i = 0;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return NULL;
}
/* 2. 检查参数是否合法 */
if (pos == -1) /* pos = -1 表示查找的是头节点 */
{
return H;
}
/* 这里可以防止参数非法,而返回 H 的情况,可以防止删除节点函数误删 */
if (pos < -1)
{
printf("[ERROR]get pos is invalid\n");
return NULL;
}
/* 3. 开始遍历节点,查找所需节点 */
p = H;
i = -1; /* 一共需要循环 pos + 1 次 */
while (i < pos)
{
p = p->next;
if (p == NULL) /* 已经遍历到单链表尾部了 */
{
printf("[ERROR]get pos is invalid\n");
return NULL;
}
i++;
}
return p;
}
/**
* @Function : slinkedlist_tail_insert
* @brief : 单链表尾部插入数据
* @param H : _slinkedlink 类型,表示要插入节点的单链表的地址
* @param value: data_t 类型,表示要插入的数据
* @return : int 类型,0, 插入成功;-1, 插入失败
* @Description:
*/
int slinkedlist_tail_insert(_slinkedlink H, data_t value)
{
/* 定义两个个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 创建新节点并初始化 */
if ((p = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
{
printf("[ERROR]The new node malloc failed!\n");
return -1;
}
p->data = value;
p->next = NULL;
/* 3. 寻找尾节点 */
q = H;
while (q->next != NULL)
{
q = q->next;
}
/* 3. 插入新节点 */
q->next = p;
return 0;
}
/**
* @Function : slinkedlist_insert
* @brief : 单链表在指定位置插入节点
* @param H : _slinkedlink 类型,表示要插入节点的单链表的地址
* @param value: data_t 类型,彪悍死要插入的数据
* @param pos : int 类型,表示要插入的节点位置,pos 大于等于 0
* @return : int 类型,0, 插入成功;-1, 插入失败
* @Description:
*/
int slinkedlist_insert(_slinkedlink H, data_t value, int pos)
{
/* 定义两个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 获取插入位置的前一个节点 */
p = slinkedlist_node_get(H, pos - 1);
if (p == NULL)
{
printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
return -1;
}
/* 3. 创建新节点并初始化 */
if ((q = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
{
printf("[ERROR]The new node malloc failed\n");
return -1;
}
q->data = value;
q->next = NULL;
/* 4. 插入新节点 */
q->next = p->next;
p->next = q;
return 0;
}
/**
* @Function : slinkedlist_delete
* @brief : 删除指定位置节点
* @param H : _slinkedlink 类型,表示要删除节点的单链表的地址
* @param pos : int 类型,要删除的节点位置(pos 大于等于 0)
* @return : int 类型,0, 删除成功;-1, 删除失败
* @Description:
*/
int slinkedlist_delete(_slinkedlink H, int pos)
{
/* 定义两个单链表结构体指针变量 */
_slinkedlink p;
_slinkedlink q;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2. 获取删除位置的前一个节点 */
/* 注意在这里,不合法的 pos 范围会在获取节点函数中被处理 */
p = slinkedlist_node_get(H, pos - 1);
if (p == NULL)
{
printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
return -1;
}
if (p->next == NULL)
{
printf("delete pos is invalid!pos-1 = %d position is the last node!\n", pos - 1);
return -1;
}
/* 3. 更新节点 */
q = p->next;
p->next = q->next; /* p-> next = p-> next-> next; */
/* 4.释放删除的节点 */
printf("Single linked list node free:%d\n", q->data);
free(q);
q = NULL;
return 0;
}
/**
* @Function : slinkedlist_node_update
* @brief : 更新指定位置的节点数据
* @param H : _slinkedlink 类型,表示要更新数据的单链表的地址
* @param value: data_t 类型,表示要更新的数据
* @param pos : int 类型,要更新的节点位置(pos 大于等于 0)
* @return : int 类型, 0, 更新成功;-1, 更新失败
* @Description:
*/
int slinkedlist_node_update(_slinkedlink H, data_t value, int pos)
{
/* 定义一个单链表结构体指针变量 */
_slinkedlink p;
/* 1. 判断单链表是否存在 */
if (H == NULL)
{
printf("Single linked list is not existed!\n");
return -1;
}
if (pos < 0)
{
printf("update pos is invalid!\n");
return -1;
}
/* 2. 获取更新节点位置 */
p = slinkedlist_node_get(H, pos);
if (p == NULL)
{
printf("pos position is not existed!\n");
return -1;
}
/* 3. 更新节点 */
p->data = value;
return 0;
}
/**
* @Function : slinkedlist_length
* @brief : 获取单链表长度(不包含头节点)
* @param H : _slinkedlink 类型,表示要获取长度的单链表的地址
* @return : int 类型, 链表不存在则返回-1; 否则返回单链表长度
* @Description:
*/
int slinkedlist_length(_slinkedlink H)
{
/* 定义一个链表结构体指针变量 */
_slinkedlink p;
int len = 0;
/* 1.判断链表是否为存在, 不存在则直接返回 */
if (H == NULL)
{
printf("[WARN ]Single linked list is not existed!\n");
return -1;
}
/* 2.遍历链表 */
p = H;
while (p->next != NULL)
{
len++;
p = p->next;
}
return (len);
}
/* 测试函数实现 */
int test_slinkedlist_tail_insert()
{
_slinkedlink H;
int a[5] = {1,3,4, 5, 6};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return -1;
for(i = 0; i<5; i++)
{
slinkedlist_tail_insert(H, a[i]);
}
slinkedlist_show(H);
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
return 0;
}
void test_slinkedlist_node_get()
{
_slinkedlink H;
_slinkedlink p;
int a[5] = {1,3,4, 5, 6};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return;
for(i = 0; i<5; i++)
{
slinkedlist_tail_insert(H, a[i]);
}
slinkedlist_show(H);
for(i = -2; i<7; i++)
{
p = slinkedlist_node_get(H, i);
if (p != NULL)
printf("i = %d,value=%d\n", i, p->data);
}
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
}
void test_slinkedlist_insert()
{
_slinkedlink H;
int a[5] = {1,3,4, 5, 6};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return;
slinkedlist_insert(H, a[0], 0);
for(i = 1; i<5; i++)
{
slinkedlist_insert(H, a[i], 1);
}
slinkedlist_show(H);
slinkedlist_insert(H, 100, 2);
slinkedlist_insert(H, 200, 50);
slinkedlist_insert(H, 300, -9);
slinkedlist_show(H);
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
}
void test_slinkedlist_delete()
{
_slinkedlink H;
int a[5] = {1,3,4, 5, 6};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return;
for(i = 0; i<5; i++)
{
slinkedlist_tail_insert(H, a[i]);
}
slinkedlist_show(H);
slinkedlist_delete(H, -4);
slinkedlist_delete(H, 50);
slinkedlist_delete(H, 2);
slinkedlist_delete(H, 3);
slinkedlist_delete(H, 3);
slinkedlist_show(H);
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
}
void test_slinkedlist_node_update()
{
_slinkedlink H;
int a[5] = {1,3,4, 5, 6};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return;
for(i = 0; i<5; i++)
{
slinkedlist_tail_insert(H, a[i]);
}
slinkedlist_show(H);
slinkedlist_node_update(H, 100, 2);
slinkedlist_show(H);
slinkedlist_node_update(H, 100, 0);
slinkedlist_show(H);
slinkedlist_node_update(H, 100, 4);
slinkedlist_show(H);
slinkedlist_node_update(H, 100, 5);
slinkedlist_show(H);
slinkedlist_node_update(H, 100, -1);
slinkedlist_show(H);
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
}
void test_slinkedlist_length()
{
_slinkedlink H;
int a[6] = {1, 3, 7, 5, 6, 8};
int i = 0;
H = slinkedlist_create();
if (H == NULL)
return;
for(i = 0; i<6; i++)
{
slinkedlist_tail_insert(H, a[i]);
}
slinkedlist_show(H);
printf("len=%d\n", slinkedlist_length(H));
printf("H=%p\n", H);
H = slinkedlist_free(H);
printf("H=%p\n", H);
}2. slinkedlist.h
/** ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== =
* Copyright © hk. 2022-2025. All rights reserved.
* File name : slinkedlist.h
* Author : qidaink
* Date : 2022-10-21
* Version :
* Description:
* Others :
* Log :
* ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==
*/
#ifndef __SLINKEDLIST_H__
#define __SLINKEDLIST_H__
/* 自定义数据类型 */
typedef int data_t;
typedef struct __node
{
data_t data;
struct __node *next;
} _slinkednode, *_slinkedlink;
/* 函数声明 */
_slinkedlink slinkedlist_create(void); /* 单链表创建 */
_slinkedlink slinkedlist_free(_slinkedlink H); /* 单链表空间释放(释放整个单链表占用的内存空间) */
int slinkedlist_show(_slinkedlink H); /* 单链表数据显示 */
_slinkedlink slinkedlist_node_get(_slinkedlink H, int pos); /* 单链表节点查找(按位置查找) */
int slinkedlist_tail_insert(_slinkedlink H, data_t value); /* 单链表尾部插入数据 */
int slinkedlist_insert(_slinkedlink H, data_t value, int pos); /* 单链表在指定位置插入节点 */
int slinkedlist_delete(_slinkedlink H, int pos); /* 删除指定位置节点 */
int slinkedlist_node_update(_slinkedlink H, data_t value, int pos); /* 更新指定位置的节点数据 */
int slinkedlist_length(_slinkedlink H); /* 获取单链表长度(不包含头节点) */
int test_slinkedlist_tail_insert();/* 单链表尾部插入测试 */
void test_slinkedlist_node_get();/* 节点查找测试 */
void test_slinkedlist_insert();/* 指定位置插入测试 */
void test_slinkedlist_delete();/* 按位置删除节点测试 */
void test_slinkedlist_node_update();/* 更新节点数据测试 */
void test_slinkedlist_length();/* 获取链表长度测试 */
#endif3. main.c
#include <stdio.h>
#include "slinkedlist.h"
int main(int argc, char *argv[])
{
// test_slinkedlist_tail_insert();
// test_slinkedlist_node_get();
// test_slinkedlist_insert();
test_slinkedlist_delete();
// test_slinkedlist_node_update();
// test_slinkedlist_length();
return 0;
}4. Makefile
##============================================================================#
# Copyright © hk. 2022-2025. All rights reserved.
# File name : Makefile
# Author : qidaink
# Date : 2022-09-29
# Version :
# Description:
# Others :
# Log :
##============================================================================#
##
TARGET := main
#============================================================================#
## 工具设置
# 设置交叉编译器名称
CROSS_COMPILE :=
# 设置交叉编译器
CC := $(CROSS_COMPILE)gcc
# 编译选项
CFLAGS += -g -Wall
#============================================================================#
## 目录路径设置
# 头文件路径设置(.h)
INCDIRS := .
# 源文件路径设置(.S .C)
SRCDIRS := .
# 生成的中间 .o 文件的位置
OBJDIRS := .
#============================================================================#
# 生成编译器的 -I dir_path 参数
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
# 获取所有的 .C 文件名称(包含路径)
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
# 获取所有的 .C 文件名称(不包含路径)
CFILENDIR := $(notdir $(CFILES))
# 指定所有的 .C 生成的 .o 文件名(包含将要存放的路径)
COBJS := $(patsubst %, $(OBJDIRS)/%, $(CFILENDIR:.c=.o))
# 将所有要生成的 .o 文件合并到一个变量中(都包含路径)
OBJS := $(COBJS)
# 设置源文件路径
VPATH := $(SRCDIRS)
#============================================================================#
# 生成目标文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $^ -o $(TARGET)
# 隐含规则推导相关中间文件
$(COBJS) : $(OBJDIRS)/%.o : %.c
$(CC) $(CFLAGS) -c $(INCLUDE) $< -o $@
#============================================================================#
.PHONY: clean clean_o
# 删除所有编译链接生成的文件命令
clean: clean_o
@rm -vf $(TARGET)
# 删除所有的 .o 文件命令
clean_o:
@rm -vf $(COBJS)
printf:
@echo "CFLAGS = $(CFLAGS)"
@echo "INCDIRS = $(INCDIRS)"
@echo "SRCDIRS = $(SRCDIRS)"
@echo "INCLUDE = $(INCLUDE)"
@echo "CFILES = $(CFILES)"
@echo "CFILENDIR = $(CFILENDIR)"
@echo "COBJS = $(COBJS)"
@echo "OBJS = $(OBJS)"
@echo "VPATH = $(VPATH)"