通用单向链表的实现(附使用例子)

当我打开csdn,发现我有第一个粉丝了,激动得差点大喊“Hello World”,整晚都很开心,哈哈,为了保住我唯一的粉丝,我决定,以后勤快一点。今天就先出一篇通用链表实现的博客,只包含了一个头文件(.h)和一个源文件(.c),可以很方便地添加到你们的项目中。

list.h:

#ifndef __LIST_H__
#define __LIST_H__

#define LIST_NO_MEM  -2
#define LIST_ERROR   -1
#define LIST_SUCCESS  0

typedef struct _node node_t;
/*node*/
struct _node{
    node_t *next;       //next node
    void *element;      //pointer of elt
};

/*list*/
typedef struct _list{
    int nb_elt;        //number of element of list
    node_t *node;      //head pointer
}list_t;

/*initialise the list,the list shouldn't be null*/
int list_init(list_t *list);

/*get the size of a list of element*/
int list_size(const list_t *list);

/*add a element to list*/
int list_add(list_t *list,void *element,int pos);

/*get an element from a list*/
void *list_get(const list_t *list,int pos);

/*remove an element from a list*/
int list_remove(list_t *list,int pos);

#endif

 

list.c

#include "list.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
int list_init(list_t *list)
{
    if(list == NULL)
        return LIST_ERROR;
    memset(list,0,sizeof(list_t));
    return LIST_SUCCESS;
}

int list_size(const list_t *list)
{
    if(list == NULL)
        return LIST_ERROR;
    return list->nb_elt;
}

/*index start from 0*/
int list_add(list_t *list,void *element,int pos)
{
    int i = 0;
    node_t *new_node = NULL;
    node_t *node = NULL;
    if(list == NULL)
        return LIST_ERROR;
    
    new_node = (node_t*)malloc(sizeof(node_t));
    if(new_node == NULL)
        return LIST_NO_MEM;
    memset(new_node,0,sizeof(node_t));
    new_node->element = element;

    if(list->nb_elt == 0)
    {
        new_node->next = NULL;
        list->node = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }
    if(pos < 0 || pos >= list->nb_elt)
        pos = list->nb_elt;

    node = list->node;
    
    if(pos == 0)
    {
        new_node->next = list->node;
        list->node = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }

    while(i + 1 < pos)
    {
        i++;
        node = node->next;
    }
    if(pos == list->nb_elt)
    {
        new_node->next = NULL;
        node->next = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }
    new_node->next = node->next;
    node->next->next = new_node;
    list->nb_elt++;
    
    return list->nb_elt;
}

void *list_get(const list_t *list,int pos)
{
    int i = 0;
    node_t *node = NULL;
    if(list == NULL)
        return NULL;
    if(pos < 0 || pos >= list->nb_elt)
        return NULL;
    node = list->node;
    while(i < pos)
    {
        i++;
        node = node->next;
    }
    
    return node->element;
}

int list_remove(list_t *list,int pos)
{
    int i = 0;
    node_t *node = NULL;
    node_t *del_node = NULL;
    if(list == NULL)
        return LIST_ERROR;
    if(pos < 0 || pos >= list->nb_elt)
        return LIST_ERROR;
    node = list->node;
    /*special case*/
    if(pos == 0)
    {
        list->node = node->next;
        list->nb_elt--;
        free(node);
        return LIST_SUCCESS;
    }

    while(pos > i + 1)
    {
        i++;
        node = node->next;
    }
    del_node = node->next;
    node->next = node->next->next;
    list->nb_elt--;

    free(del_node);
    return LIST_SUCCESS;
    
}
#include <stdlib.h>
#include <stdio.h>
#include "list.h"


int main()
{
	int size = 0;
	int i = 0;
	list_t list;
	char *elt;
	list_init(&list);
	char arr1[5] = "hello";
	char arr2[5] = "world";
	char arr3[32] = "i'm pumpkin monkey";
	list_add(&list,(void*)arr1,-1);
	list_add(&list,(void*)arr2,-1);
	list_add(&list,(void*)arr3,-1);
	printf("list size:%d\n",list_size(&list));
	list_remove(&list,1);
	size = list_size(&list);
	printf("list size:%d\n",size);
	for(i = 0;i<size;i++)
	{
		elt = (char*)list_get(&list,i);
		if(elt == NULL)
		{
			printf("get null node\n");
		}
		printf("elment :%s\n",elt);
	}
	return 0;
}

以上就是最基本的通用单向链表的实现,包含了创建、添加、删除、长度、获取等最常用的基本功能。还可以包含克隆,下个元素、等其他功能,可根据需要自己添加

实际上链表并不复杂,但是很多人还是没法写好,主要还是特殊情况(边界条件)考虑得不够充分。不仅是在链表,在任何一个函数的实现里,都必须要考虑特殊场景。对于链表来说,其实需要注意的也就那几个点,目标节点(pos)是否存在、链表长度为0和pos刚好等于链表长度、输入链表是否有效、循环条件。

大家可以考虑加个头结点:

数据结构中,在单链表的开始结点之前附设一个类型相同的结点,称之为头结点。头结点的数据域可以不存储任何信息,头结点的指针域存储指向开始结点的指针(即第一个元素结点的存储位置)。

头结点作用:

1、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.如果当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL.

2、是为了方便单链表的特殊操作,插入在表头或者删除第一个结点.这样就保持了单链表操作的统一性!

3、单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会。

4、对单链表的多数操作应明确对哪个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表因为有头结点,首元结点、中间结点的操作相同 ,从而减少分支,使算法变得简单 ,流程清晰。对单链表进行插入、删除操作时,如果在首元结点之前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在C 算法的函数形参表中头指针一般使用指针的指针(在C+ +中使用引用 &);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可。

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HJ212是一种污染物在线监测仪器设备的数据通信协议,其组帧格式是固定的,由起始符、长度、命令型、数据域、校验码和结束符组成。具体的组帧格式可以参考HJ212协议文档。 下面是一个使用单向链表实现HJ212组帧的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define HJ212_START_CHAR 0x3A // 起始符 #define HJ212_END_CHAR 0x0D // 结束符 typedef struct hj212_frame { int len; // 长度 char cmd[4]; // 命令型 char* data_field; // 数据域 char checksum; // 校验码 } hj212_frame_t; // 计算校验码 char hj212_checksum(char* data_field, int len) { char checksum = 0; for (int i = 0; i < len; i++) { checksum ^= data_field[i]; } return checksum; } // 创建一个HJ212帧 hj212_frame_t* hj212_create_frame(char* cmd, char* data_field) { hj212_frame_t* frame = (hj212_frame_t*)malloc(sizeof(hj212_frame_t)); if (frame == NULL) { return NULL; } frame->len = strlen(data_field); strncpy(frame->cmd, cmd, 4); frame->data_field = (char*)malloc(frame->len); strncpy(frame->data_field, data_field, frame->len); frame->checksum = hj212_checksum(frame->data_field, frame->len); return frame; } // 将HJ212帧转换为字符串 char* hj212_frame_to_string(hj212_frame_t* frame) { char* str = (char*)malloc(frame->len + 9); sprintf(str, "%c%04X%s%c%c", HJ212_START_CHAR, frame->len, frame->cmd, frame->checksum, HJ212_END_CHAR); return str; } int main() { char cmd[] = "8103"; // 命令型 char data_field[] = "010000"; // 数据域 hj212_frame_t* frame = hj212_create_frame(cmd, data_field); if (frame == NULL) { printf("Failed to create HJ212 frame.\n"); return -1; } char* str = hj212_frame_to_string(frame); printf("HJ212 frame: %s\n", str); free(frame->data_field); free(frame); free(str); return 0; } ``` 在上面的示例代码中,我们定义了一个HJ212帧的结构体,包含长度、命令型、数据域、校验码等信息。`hj212_create_frame`函数用于创建一个HJ212帧,该函数接受命令型和数据域作为参数,并返回一个指向HJ212帧结构体的指针。`hj212_frame_to_string`函数将HJ212帧转换为字符串格式。 使用单向链表实现HJ212组帧需要根据具体的应用场景来设计链表节点。在本例中,我们可以将HJ212帧作为链表节点的数据域,将指向下一个节点的指针作为链表节点的指针域。每次创建一个HJ212帧时,我们将其作为一个新的链表节点插入到链表的末尾。对于HJ212帧的发送和接收,我们可以使用串口通信或者TCP/IP通信等技术来实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值