最近,当在c中编写一些linux程序时,似乎很多地方需要一个可以支持不同类型值的公共链表,因此我试图实现一个,但仍然存在一些问题。
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();
}
代码列表:
编译 - 测试:
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);
答案 0 :(得分:1)
我不确定你想做什么,但我建议:
不要在.h
文件中导出列表的定义。用户不需要知道实现。
请注意,您的struct llist_item
只能包含一个字符。将其更改为void *
,以便它可以保存指向任何类型值的指针。
必须传递llist_append
中的结构大小似乎很奇怪,因为这是列表结构的大小加上要存储的值的大小。但这与您想要存储“任何价值”
通常,用户希望您维护指向列表必须存储的数据的指针。您的.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)
这种方法有几个问题:
char
数组后struct llist_item *
数组的编译器假定的对齐方式不同。因此,即使在结构中使用可变大小的尾随数组也无法提供可行的解决方案。您可以使用void *
并单独分配值,但这很浪费,或使用宏来实现不同类型的源代码模板,但这很麻烦且容易出错。