通用链表?

时间:2018-04-26 12:18:41

标签: c linked-list polymorphism

我想知道是否可以在C中使用通用函数用于链接列表,(我不想在C ++中但在C中这样做)示例:

struct first_struct
{
    struct first_struct *next;
    int a;
    int b;
};
struct second_struct
{
     struct second_struct *next;
     int a;
     int b;
     int c; // just one more variable than first-struct
};

我强迫每次为两个列表创建一个函数:

add_node(struct first_struct *mystruct)// doesn't matter the function here juste let's assume they add correctly a node
add_node1(struct second_struct *mystruct)
//and so on each time i want to make some others function always make them twice

还是有更好的方法吗?

4 个答案:

答案 0 :(得分:6)

更好的方法是抽象出链接处理(使结构成为列表节点的原因),然后通过使用节点结构启动每个可列表结构来重用它。

像这样:

struct list_node {
  struct list_node *next;
};


struct first_struct {
  struct list_node list_node;
  int a;
  int b;
};


struct second_struct {
  struct list_node list_node;
  int a;
  int b;
  int c;
};

然后创建处理(指向)struct list_node的列表函数。

这通常被称为“侵入式列表”,因为它要求应用程序级数据结构“知道”可以将其放入列表中。它还意味着一个结构的实例一次只能在一个列表中。

另一种方法是创建一个仅处理数据指针(void *)的列表库,它可以消除限制,但会带来其他限制(更多的堆分配,当数据很小时很烦人)。

答案 1 :(得分:1)

就个人而言,我已经实现了一个通用的链表:它通过提供一个比较两个节点的函数和一个用于销毁节点的可选函数(自由字符串,关闭文件等)来工作。

#include <stdbool.h>
#include <stddef.h>

typedef struct link {
    void        *data;
    struct link *previous;
    struct link *next;
} link_s;

typedef struct list {
    link_s *head;
    link_s *tail;
    size_t nbLink;

    /* function pointer */
    int    (*Data_Compare)(const void *data1, const void *data2);
    void   (*Data_Destructor)(void *data);
} list_s;

#define LIST_CONSTRUCTOR(f_compar, f_destructor) {.head = NULL, \
                                                  .tail = NULL, \
                                                  .nbLink = 0, \
                                                  .Data_Compare = f_compar, \
                                                  .Data_Destructor = f_destructor}

void List_Constructor(list_s *self, int (*Data_Compare)(const void *data1, const void *data2), void (*Data_Destructor)(void *data));
void List_Destructor(list_s *self);

bool List_Add(list_s *self, void *data);

void *List_RemoveByLink(list_s *self, link_s *link);
void *List_RemoveByData(list_s *self, void *data);
void *List_RemoveByCondition(list_s *self, bool (*Data_Condition)(const void *data));

void List_DestroyByLink(list_s *self, link_s *link);
void List_DestroyByData(list_s *self, void *data);

void List_DestroyByCondition(list_s *self, bool (*Data_Condition)(const void *data));

void List_Sort(list_s *self);
void List_Merge(list_s *to, list_s *from);
void List_Reverse(list_s *self);

这样,您可以将任何想要的内容添加到列表中。只要注意有一个propre比较函数和销毁函数。

答案 2 :(得分:1)

您可以使用结构中的void指针轻松实现通用链表。

以下是我创建的此类列表的示例:

<强> list.c

#include <stdlib.h>
#include "list.h"
#include <malloc.h>
#include <stdio.h>
#include <string.h>

void listNew(list* list, unsigned int elementSize, freeMemory freeFn,        
printList print) {

    list->numOfElem = 0;
    list->freeFn = freeFn;
    list->pr = print;
    list->head = NULL;
    list->sizeOfElem = elementSize;
}

node * listPushFront(list *list, void* data) {

    node *listNode = (node*)malloc(sizeof(node));

    if (listNode == NULL) {

        return NULL;
    }

    listNode->object = malloc(sizeof(list->sizeOfElem));

    if (listNode->object == NULL) {

        return NULL;
    }

    memcpy(listNode->object, data, list->sizeOfElem);

    listNode->pNext = list->head;
    list->head = listNode;

    list->numOfElem++;

    return listNode;
}

void listDestroy(list *list)
{
    node *current;

    while (list->head != NULL) {
        current = list->head;
        list->head = current->pNext;

        if (list->freeFn) {
            list->freeFn(current->object);
        }

        free(current->object);
        free(current);
    }
}

void listPrint(list *l) {

    node* temp = l->head;
    int i = 0;

    if (temp == NULL) {

        printf("\nEmpty list.");
        return;
    }

    while (temp) {

        printf("\nPrint element %d", i);

        l->pr(temp->object);
        temp = temp->pNext;

        i++;
    }
}

<强> list.h

#ifndef __LIST_H
#define __LIST_H

typedef void(*freeMemory)(void*);
typedef void(*printList)(void*);

typedef struct _node {

    void* object;
    struct _node* pNext;

}node;

typedef struct _list {

    unsigned int sizeOfElem;
    unsigned int numOfElem;
    node* head;
    freeMemory freeFn;
    printList pr;

}list;

void listNew(list* list, unsigned int elementSize, freeMemory freeFn,             
printList print);
node * listPushFront(list *list, void* data);
void listDestroy(list *list);
void listPrint(list *l);

#endif

<强>的main.c

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

typedef struct _TLV {

    unsigned int tag;
    unsigned int length;
    unsigned char* value;

}TLV;


void listFree(void* data) {

    TLV** ptr = (TLV**)data;

    free((*ptr)->value);
}

void Print(void* data) {

    TLV** ptr = (TLV**)data;

    printf("\nTag = %d", (*ptr)->tag);
    printf("\nLength = %d", (*ptr)->length);
    printf("\nValue = ");

    for (int i = 0; i < (*ptr)->length; i++) {

        printf("%d", (*ptr)->value[i]);
    }
}

TLV* allocateTLV(unsigned int tag, unsigned int length, unsigned char* 
value) {

    TLV* elem = (TLV*)malloc(sizeof(TLV));

    if (elem == NULL) {

        return NULL;
    }

    elem->tag = tag;
    elem->length = length;
    elem->value = (unsigned char*)malloc(length);

    if (value == NULL) {

        return NULL;
    }

    memcpy(elem->value, value, length);

    return elem;
}

int main()
{
    list l;
    TLV* tag;

    unsigned char test2[2] = { 1,2 };
    unsigned char test3[3] = { 1,2,3 };
    unsigned char test4[4] = { 1,2,3,4};

    listNew(&l, sizeof(TLV*), listFree, Print);

    tag = allocateTLV(2, sizeof(test2), test2);
    if (tag != NULL) {

        listPushFront(&l, &tag);
    }

    tag = allocateTLV(3, sizeof(test3), test3);
    if (tag != NULL) {

        listPushFront(&l, &tag);
    }

    tag = allocateTLV(4, sizeof(test4), test4);
    if (tag != NULL) {

        listPushFront(&l, &tag);
    }

    listPrint(&l);

    listDestroy(&l);

    return 0;
}

main.c是创建struct TLV指针列表的一个示例。

答案 3 :(得分:1)

您可以使用C的两个功能实现通用链表,即void指针和函数指针。

后者(函数指针)对于构建链表并不重要,但是如果你想对链表的数据做一些有用的事情,比如印刷,这一点至关重要。

以下是一个完整的工作示例:

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


typedef struct node {
    void *data;
    struct node *next;
} node;


int lst_nodeAdd(
    node **head,
    node **tail,
    const void *data,
    size_t szData);

void lst_nodePrint(
    node *head,
    void(*print)(const void *));

void lst_nodeFree(node *head);

/* PRINTING FUNCTIONS */
void print_int(const void *a);
void print_string(const void *str);


int main(void)
{
    const char *str[] = {
        "0x0001",
        "0x0002",
        "0x0003",
    };

    // head & tail
    node *head = NULL; 
    node *tail = NULL;

    // List of strings
    lst_nodeAdd(&head, &tail, str[0], strlen(str[0]) + 1);
    lst_nodeAdd(&head, &tail, str[1], strlen(str[1]) + 1);
    lst_nodeAdd(&head, &tail, str[2], strlen(str[2]) + 1);

    lst_nodePrint(head, print_string);
    lst_nodeFree(head);
    head = NULL;
    tail = NULL;
    //....................................................

    // List of ints
    int int_array[] = {
        0,
        1,
        2,
    };
    lst_nodeAdd(&head, &tail, &int_array[0], sizeof(int));
    lst_nodeAdd(&head, &tail, &int_array[1], sizeof(int));
    lst_nodeAdd(&head, &tail, &int_array[2], sizeof(int));

    lst_nodePrint(head, print_int);
    lst_nodeFree(head);
    head = NULL;
    tail = NULL;


    system("PAUSE");
    return 0;
}


int lst_nodeAdd(
    node **head,
    node **tail,
    const void *data,
    size_t szData)
{
    void *tmp;
    tmp = malloc(sizeof(node));

    if (!tmp)
    {
        return 0;
    }

    ((node *)tmp)->next = NULL;
    ((node *)tmp)->data = malloc(szData);

    if (!((node *)tmp)->data)
    {
        free(tmp);
        return 0;
    }

    memcpy(((node *)tmp)->data, data, szData);

    if (!*head)
    {
        *head = (node *)tmp;
    }
    else
    {
        (*tail)->next = (node *)tmp;
    }
    *tail = (node *)tmp;
    return 1;
}

void lst_nodePrint(
    node *head,
    void(*print)(const void *))
{
    while (head)
    {
        print(head->data);
        head = head->next;
    }
}

void lst_nodeFree(node *head)
{
    node *tmp = head;
    while (head)
    {
        head = head->next;
        free(tmp->data);
        free(tmp);
        tmp = head;
    }
}

void print_int(const void *a)
{
    printf("%d\n", *(const int *)a);
}

void print_string(const void *str)
{
    puts((const char *)str);
}