次のようなデータ構造、リンクリストがあります
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);
最初のアプローチが機能しないのに、2 番目のアプローチが機能する理由を説明できません。
一般的なスタイルに関して、このアプローチは一般的ですか? または、C でデータ構造を初期化するためのより良い、より一般的な方法はありますか? 私は比較的新しい C プログラマーであり、コンストラクターでそのような初期化を行うことを期待するオブジェクト指向言語から来ており、C でそのスタイルをコピーしようとしています。必ずしも論理的ではない?