实现具有单个值字段

时间:2016-01-23 10:42:58

标签: c linux struct linked-list

最近,当在c中编写一些linux程序时,似乎很多地方需要一个可以支持不同类型值的公共链表,因此我试图实现一个,但仍然存在一些问题。

方法

  • 使用指针定义结构,然后以char []类型的值字段结束, 将它用作通用结构,
  • 定义&链接列表上的impl方法,使用通用结构,
  • 定义具有不同类型的值字段的新结构类型
  • 调用函数时,只需将事物转换为通用类型,

代码:草稿版本

linked_list.h

#ifndef _LINKED_LIST
#define _LINKED_LIST

// common list for any type,
struct llist_item {
    struct llist_item *next;
    char value[1];
};

// int list
struct llist_int {
    struct llist_int *next;
    int value;
};

/**
 * append item to end of list,
 * 
 * @param headp
 *  pointer to head pointer,
 * @param valuep
 *  pointer set value of deleted item into,
 * @param value_size
 *  size of value,
 * @param struct_size
 *  size of actual struct,
 * 
 * @return
 *  pointer to head,
 */
extern struct llist_item *llist_append(struct llist_item **headp, void *valuep, ssize_t value_size, ssize_t struct_size);

/**
 * delete head,
 * 
 * @param headp
 *  pointer to head pointer,
 * @param valuep
 *  pointer set value of deleted item into,
 * 
 * @return
 *  pointer to new head,
 */
extern struct llist_item *llist_del_head(struct llist_item **headp, char *valuep);

#endif

linked_list.c

// linked_list utility
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <stdlib.h>

#include "linked_list.h"

/*
   printf("error while linked_list: %s\n", strerror(errno));
   printf("linked_list succeed\n");
 */

struct llist_item *llist_append(struct llist_item **headp, void *valuep, ssize_t value_size, ssize_t struct_size) {
    struct llist_item *head = *headp;

    // create new item
    struct llist_item *new_item = (struct llist_item*) malloc(struct_size);
    new_item->next = NULL;
    memcpy(&(new_item->value), valuep, value_size);

    // append new item
    if(head == NULL) { // empty queue,
        head = new_item;
        *headp = head;
    } else {
        // find last item
        struct llist_item *tail = head;
        while(tail->next != NULL) {
            tail = tail->next;
        }   

        tail->next = new_item;
    }

    return head;
}

struct llist_item *llist_del_head(struct llist_item **headp, char *valuep) {
    struct llist_item *head = *headp;

    if(head == NULL) {
        return NULL;
    } else {
        memcpy(valuep, &(head->value), sizeof(*valuep));
        *headp = head->next;
        free(head);
        return *headp;
    }
}

llist_test.c

// linked_list test
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <stdlib.h>

#include "linked_list.h"

int linked_list_test() {
    struct llist_int *int_list = NULL; // it's important to initialize this pointer as NULL explicitly,
    int i;

    for(i=1; i<=5; i++) {
        llist_append((struct llist_item **) &int_list, (void *) &i, sizeof(int), sizeof(struct llist_int));
    }

    struct llist_int *int_item;
    int value;
    if(int_list != NULL) {
        do {
            (struct llist_int *)llist_del_head((struct llist_item **) &int_list, (char *) &value);
            printf("%d\n", value);
        } while (int_list!= NULL);
    }

    return 0;
}

int main(int argc, char * argv[]) {
    return linked_list_test();
}

编译&amp;运行

代码列表

  • linked_list.h ,标题,
  • linked_list.c ,实施,
  • llist_test.c ,做测试,

编译 - 测试:

  

gcc -Wall linked_list.c llist_test.c -o a.out

执行:

  

./ a.out的

问题:

  • 铸造是复杂的,有没有一些方法来简化它?
  • 在测试方法linked_list_test()中:

    如果改变:

        do {
            int_item = (struct llist_int *)llist_del_head((struct llist_item **) &int_list, (char *) &value);
            printf("%d\n", value);
        } while (int_item != NULL);
    

        do {
            (struct llist_int *)llist_del_head((struct llist_item **) &int_list, (char *) &value);
            printf("%d\n", value);
        } while (int_list!= NULL);
    

    然后结果是运行,而不是输出:

      

    1 2 3 4 5

    输出:

      

    32513 32514 32515 32516 32517

    差异是指针转换,为什么它会使结果不同?

@Update - 关于第二个问题

正如@BLUEPIXY在评论中所描述的那样,sizeof(*valuep)确实导致了这个问题,现在我修改了llist_del_head(),并明确地提供了参数列表中的大小,并发布了修复版。

该功能现在如下所示:

extern struct llist_item *llist_del_head(struct llist_item **headp, char *valuep, ssize_t value_size);

2 个答案:

答案 0 :(得分:1)

我不确定你想做什么,但我建议:

  1. 不要在.h文件中导出列表的定义。用户不需要知道实现。

  2. 请注意,您的struct llist_item只能包含一个字符。将其更改为void *,以便它可以保存指向任何类型值的指针。

  3. 必须传递llist_append中的结构大小似乎很奇怪,因为这是列表结构的大小加上要存储的值的大小。但这与您想要存储“任何价值”

  4. 的方式有关

    通常,用户希望您维护指向列表必须存储的数据的指针。您的.h文件可能看起来像:

    /* append pointer valuep to the list */
    void *llist_append(void **list, void *valuep, ssize_t value_size);
    
    /* delete the list element that contains pointer valuep */
    void *llist_del_head(void **list, void *valuep);
    

    和您的实施:

    // common list for any type,
    struct llist_item {
        struct llist_item *next;
        void *value;
    };
    

    并追加:

    // create new item
    struct llist_item *new_item = malloc(sizeof(struct llist_item));
    new_item->next = NULL;
    new_item->value= valuep;
    

答案 1 :(得分:1)

这种方法有几个问题:

  • 您在结构中留出的空间只有1个字节,您不能将大于1个字节的类型存储到其中。
  • 如果你要在那里为更大的类型留出空间,你仍然会遇到对齐问题:较大的类型可能需要与char数组后struct llist_item *数组的编译器假定的对齐方式不同。因此,即使在结构中使用可变大小的尾随数组也无法提供可行的解决方案。

您可以使用void *并单独分配值,但这很浪费,或使用宏来实现不同类型的源代码模板,但这很麻烦且容易出错。

对于这种概念,C没有正确的结构,C ++的代价是更加复杂。