C语言中的链表作为一个重要的知识点,也是必须要理解和会使用的,在此将自己的理解和学习过程做个记录。
个人觉得在进行链表的相关操作时首先要理解链表分三种,单向链表、双向链表、循环链表。
虽然说是三种,但其实本质都是一样的,就是在一个结构体里面有数据域和指针域,数据域中用来描述节点的信息,而指针域中用来指向上一个节点或者下一个节点,可以通俗的理解为就相当于一个盒子分成两部分。
以下是我实测过得代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//创建数据结构体
typedef struct message{
int class;
int age;
}person;
//创建一个链表数据
typedef struct NODE{
person room1;
struct NODE * next;
}node,*list;
//创建一个链表头
list creat(void)
{
list head =(list)malloc(sizeof(node));
head->next =NULL;
return head;
}
//增加一个节点
bool add_node(list head,int type)
{
list pi =(list)malloc(sizeof(node));
if(type ==1)
{
person room2;
room2.class =9;
room2.age =18;
pi->room1=room2;
}
else if(type ==2)
{
person room3;
room3.class =8;
room3.age =24;
pi->room1=room3;
}
else
{
person room5;
room5.class=5;
room5.age =30;
pi->room1=room5;
}
pi->next=head->next;
head->next=pi;
return 1;
}
//删除一个节点
bool del_node(list head)
{
list prev =head;
list p =prev->next;
while(NULL != p)
{
//if(!strcmp(p->room1.age,24))
if(p->room1.age==24)
{
prev->next =p->next;
free(p);
return 1;
}
prev= p;
p =p->next;
}
return 0;
}
int main(int argc, char **argv)
{
person room4;
room4.class=7;
room4.age=20;
list head=creat();
add_node(head,1);
printf("head.name= %d\n",head->next->room1.age);
add_node(head,2);
printf("head.name= %d\n",head->next->room1.age);
printf("head.name= %d\n",head->next->next->room1.age);
add_node(head,3);
printf("head.name= %d\n",head->next->room1.age);
printf("head.name= %d\n",head->next->next->room1.age);
printf("head.name= %d\n",head->next->next->next->room1.age);
del_node(head);
printf("head.name= %d\n",head->next->room1.age);
printf("head.name= %d\n",head->next->next->room1.age);
return 0;
}
//打印结果为 18 24 18 30 24 18 30 18
这个代码中要注意,字符串的赋值如果直接赋值会报错。
struct student{
char name[10];
}
int main()
{
struct student stu1;
stu1.name ="xiaohu";//error 这样直接赋值是错误的 要用strcpy 字符串拷贝
char *pname ="xiaohu";
strcpy(stu1.name,pname);//ok
}
所以,修改后的代码为头插法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct class{
char name[10];
int class;
int age;
}Student;
typedef struct NODE{
Student stu;
struct NODE * next;
struct NODE * front;
}node,*list;
list creat(void)
{
list head =(list)malloc(sizeof(node));
head->next =NULL;
return head;
}
bool add_node(list head,int type)
{
list pi =(list)malloc(sizeof(node));
if(type ==1)
{
Student stu1;
stu1.class =9;
stu1.age =18;
char *pname1 = "xiaohu";
strcpy(stu1.name,pname1);
pi->stu=stu1;
}
else if(type ==2)
{
Student stu2;
stu2.class =8;
stu2.age =24;
char *pname2 ="leyan";
strcpy(stu2.name,pname2);;
pi->stu=stu2;
}
else
{
Student stu3;
stu3.class=5;
stu3.age =30;
char *pname3 ="rooik";
strcpy( stu3.name,pname3);
pi->stu=stu3;
}
pi->next=head->next;
head->next=pi;
return 1;
}
bool del_node(list head)
{
list prev =head;
list p =prev->next;
while(NULL != p)
{
//if(!strcmp(p->room1.age,24))
if(p->stu.age==24)
{
prev->next =p->next;
free(p);
if(p=!NULL)
p=NULL;
return 1;
}
prev= p;
p =p->next;
}
return 0;
}
int main(int argc, char **argv)
{
Student stu4;
stu4.class=7;
stu4.age=20;
list head=creat();
add_node(head,1);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
add_node(head,2);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->next->stu.name,head->next->next->stu.age,head->next->next->stu.class);
add_node(head,3);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->next->stu.name,head->next->next->stu.age,head->next->next->stu.class);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->next->next->stu.name,head->next->next->next->stu.age,head->next->next->next->stu.class);
del_node(head);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->next->stu.name,head->next->next->stu.age,head->next->next->stu.class);
return 0;
}
使用链表时要注意分配空间和释放,主义判断链表节点的一些关键信息,比如链表是否为空,当前节点是否存在,是头结点 尾结点还是中间节点等,我上面的程序只代表了最简单的情况,并没有考虑到实际应用中的各种情况,这一点需要注意。
1 链表要初始化,链表要记住头结点
2 每新建一个节点都需要分配内存,用malloc来分配,注意判断是否分配成功。
3 增加节点和删除节点时最好画图来理解和实操。
4 链表中可以增加其他数据成员,比如链表节点个数,等等。
经过修改后版本是
1 src/app.c
/**@file app.c
* @note
* @brief 测试代码
*
* @author luoxi11
* @date 20220707
* @version V1.0.0
*
* @note ///Description here
* @note History:
* @note <author> <time> <version > <desc>
* @note
* @warning
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <log_level.h>
#include <stdbool.h>
typedef struct class{
char name[16];
int class;
int age;
}Student;
/* 定义好链表结构体 */
typedef struct NODE{
Student stu;
struct NODE * next;
struct NODE * front;
}node,*list;
/* 创建一个节点数据 */
Student creat_stu(void)
{
Student new_stu;
new_stu.class =9;
new_stu.age =18;
char *pname = "xiaohu";
strcpy(new_stu.name,pname);
return new_stu;
}
/*创建一个头结点*/
list creat_head(void)
{
list head =(list)malloc(sizeof(node));
head->next =NULL;
return head;
}
/* 头插法新增一个节点 */
bool add_node(list head,Student st)
{
list new_node =(list)malloc(sizeof(node));
Student *st1=NULL;
st1=&st;
if(NULL!= st1)
{
new_node->stu=st;
new_node->next=head->next;
head->next=new_node;
return 1;
}
else
{
LOG_ERROR("add_node error!\r\n");
return 0;
}
}
/* 删除一个固定的节点 */
bool del_node(list head,int age)
{
list prev =head; //定义一个节点一个人指向头节点
list p =prev->next; //定义一个节点一个人指向头节点的下一个节点
while(NULL != p)
{
if(p->stu.age >= age)
{
prev->next =p->next;
free(p);
if(NULL!=p)
{
p=NULL;
}
return 1;
}
prev= p;
p =p->next;
}
LOG_ERROR("del_node failed\r\n");
return 0;
}
int main(int argc, char **argv)
{
Student stu =creat_stu();
list head=creat_head();
add_node(head,stu);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
add_node(head,stu);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->stu.name,head->next->stu.age,head->next->stu.class);
printf("head.name= %s head.age = %d head.class =%d\n",head->next->next->stu.name,head->next->next->stu.age,head->next->next->stu.class);
return 0;
}
include/log_level.h
/**@file log_level.h
* @note
* @brief
*
* @author luoxi
* @date 20221014
* @version 1.0
*
* @note 设置日志打印等级
* @note History:
* @note <author> <time> <version > <desc>
* @note
* @warning
*/
#ifndef _LOG_LEVEL_H_
#define _LOG_LEVEL_H_
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdio.h>
#define __LOG_CN__ 1
#if __LOG_CN__
#define COLOR_END "\033[0m"
#define INFO_COLOR "\033[36m"
#define DEBUG_COLOR "\033[35m"
#define DEBUG_ARRAY_COLOR "\033[35m"
#define WARNING_COLOR "\033[33m"
#define ERROR_COLOR "\033[31m"
#define LOG_LEVEL 1
typedef enum
{
//用户可自定义打印格式
LOG_LEVEL_CUSTOM = 0,
//打印错误
LOG_LEVEL_ERROR,
//打印警告
LOG_LEVEL_WARNING,
//打印调试信息
LOG_LEVEL_DEBUG,
//打印数组信息
LOG_LEVEL_DEBUG_ARRAY,
//打印信息
LOG_LEVEL_INFO,
}log_level_enum_t;
#define LOG_INFO(format,...) do{\
if (LOG_LEVEL >= LOG_LEVEL_INFO)\
{\
printf(INFO_COLOR"[INFO]:[FUNC]:%s, [LINE]:%04d:"format"\r\n"COLOR_END, __func__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_DEBUG(format,...) do{\
if (LOG_LEVEL >= LOG_LEVEL_DEBUG)\
{\
printf(DEBUG_COLOR"[DEBUG]:[FUNC]:%s, [LINE]:%04d:"format"\r\n"COLOR_END, __func__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_DEBUG_ARRAY(array, num) do{\
if (LOG_LEVEL >= LOG_LEVEL_DEBUG_ARRAY)\
{\
uint32_t i;\
uint8_t *a = array;\
printf(DEBUG_ARRAY_COLOR"[DEBUG_ARRAY]:[FUNC]:%s, [LINE]:%04d:\r\n"COLOR_END, __func__, __LINE__);\
for (i = 0; i < num; i++)\
{\
printf("%#X ", a[i]);\
if ((i + 1 ) % 10 == 0)\
{\
printf("\r\n");\
}\
}\
printf("\r\n");\
}\
}while(0)
#define LOG_WARNING(format,...) do{\
if (LOG_LEVEL >= LOG_LEVEL_WARNING)\
{\
printf(WARNING_COLOR"[WARNING]:[FUNC]:%s, [LINE]:%04d:"format"\r\n"COLOR_END, __func__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_ERROR(format,...) do{\
if (LOG_LEVEL >= LOG_LEVEL_ERROR)\
{\
printf(ERROR_COLOR"[ERROR]:[FUNC]:%s, [LINE]:%04d:"format"\r\n"COLOR_END, __func__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_CUSTOM(format,...) do{\
if (LOG_LEVEL >= LOG_LEVEL_CUSTOM)\
{\
printf(format"\r\n", ##__VA_ARGS__);\
}\
}while(0)
#else
#define LOG_INFO(format,...) do { } while (0)
#define LOG_DEBUG(format,...) do { } while (0)
#define LOG_DEBUG_ARRAY(array, num) do { } while (0)
#define LOG_WARNING(format,...) do { } while (0)
#define LOG_ERROR(format,...) do { } while (0)
#define LOG_CUSTOM(format,...) do { } while (0)
#endif
#endif
Makefile
##
## Makefile
##
## History:
## 20221014 luoxi11 TEST
## arm-ca9-linux-gnueabihf-gcc F1plus
## arm-mol-linux-uclibcgnueabihf-gcc B1Pro B2
## arm-linux-gnueabihf-gcc
default: all
PWD := $(shell pwd)
ROOTDIR = ./
# 交叉编译工具
CC = $(TOOL_PREFIX)gcc
AR = $(TOOL_PREFIX)ar
INC_DIR += -I./include/
LIBFLGS = -lpthread
TARGET = release/app_test
CURPATH = $(shell pwd)
SOURCES = $(wildcard $(CURPATH)/src/*.c)
OBJECTS = $(patsubst %.c, %.o, $(SOURCES))
.PHONY: clean all
all : $(TARGET)
%.o:%.c
$(CC) $(CFLAGS) $(INC_DIR) -c $^ -o $@
$(TARGET) : $(OBJECTS)
echo -e $(OBJECTS)
$(CC) $(OBJECTS) $(LIBFLGS) -o $(TARGET)
rm -f $(OBJECTS)
clean:
rm -f $(OBJECTS) $(TARGET)