我有一个数据结构,链表,看起来像
struct listitem {
void* data;
struct listitem* next;
};
typedef struct listitem listitem;
typedef struct {
listitem* head;
pthread_rwlock_t lock;
} linkedlist;
我正在为数据使用指向void *的指针,因为我想使数据结构具有多态性,因此我可以将它用于几个不同的应用程序。
要初始化列表(分配内存并初始化rw锁),我将它传递给一个函数 init_list(..)。当我按如下方式将指针传递给列表时,只要我尝试在列表上执行任何进一步操作(例如将项目推送到列表),程序就会挂起:
int init_list(linkedlist* list /* borrowed - list to initialise (probably unallocated) */) {
list = (linkedlist*)calloc(1, sizeof(linkedlist)); // clear the memory, so that head is a null pointer
printf("Allocated memory\n");
if (list == 0) {
perror("calloc failed on allocating memory for list");
return 1;
}
printf("Initialising lock\n");
pthread_rwlock_init(&list->lock, NULL);
return 0;
}
...
linkedlist* ll;
init_list(ll);
我的理解是上面的应该清除ll指向的内存并且恰当地初始化锁的内存位置。
当我将指针传递给指向列表的指针时,一切正常(即当我尝试执行进一步的操作,例如获取锁定并将项目推送到列表时,程序不会挂起) 。我不明白为什么添加这个额外的间接层使其工作。我是否认为实际内存位置的操作是相同的,无论我如何引用它们?
即。以下是有效的,而第一种方法并不是:
int init_list(linkedlist** list /* borrowed - list to initialise (probably unallocated) */) {
*list = (linkedlist*)calloc(1, sizeof(linkedlist)); // clear the memory, so that head is a null pointer
printf("Allocated memory\n");
if (list == 0) {
perror("calloc failed on allocating memory for list");
return 1;
}
printf("Initialising lock\n");
pthread_rwlock_init(&(*list)->lock, NULL);
return 0;
}
...
linkedlist* ll;
init_list(&ll);
我无法解释为什么第二种方法在第一种方法不起作用时会起作用。
就一般风格而言,这种做法是否常见?或者是否有更好,更常见的方法来初始化C中的数据结构?我是一个相对较新的C程序员,来自面向对象的语言,我希望在构造函数中进行这样的初始化,而且我有点试图在C中复制该样式,其中 - 思考它 - 可能不一定合乎逻辑?
答案 0 :(得分:0)
在第一个版本中,您正在做的是分配一块新内存,将其清零,并将其复制到list
参数(这不会更改ll
,因为C按值发送参数)。第二个版本有*list
,其中列表是指向ll
的指针,这意味着当您更改*list
时实际更改ll
(与第一个版本不同)。
答案 1 :(得分:0)
在第一种情况下,您将指针(ll)的副本推送到堆栈上。你的代码分配内存,我认为sizeof(slinkedlist)
是一个拼写错误,用0填充它,....然后丢弃副本。您的调用者永远不会获得更新的指针。在init之后打印ll
的值。在第二个中,您将指针指向指针,该指针将被分配/初始化,当您返回调用者时,可以愉快地使用它。您可以使用第一个版本,如果不返回0或1而返回ll
并将返回值分配给调用者中的变量。这将是一种功能性的风格。