汎用連結リストの実装

汎用連結リストの実装

汎用連結リストの設計思想は、ノード構造体を定義し、その中に前のノードと次のノードを指すポインタを含むことです。その後、具体的な要件に応じて、ノード構造体内に他のデータ構造をネストすることで、異なる種類の連結リストを実現します。

構造体メンバの先頭アドレス計算

// 構造体メンバmemberが構造体内の最初のメンバアドレスからのオフセット値を計算
#define offset(type, member) \
    ((size_t)&(((type*)0)->member))

// 構造体メンバのポインタから構造体変数の先頭アドレスを計算
#define container_of(ptr, type, member) \
    ((type*)((char*)(ptr) - offsetof(type, member)))

// 構造体ポインタobj、連結リストの先頭ノードhead、構造体メンバ名mem
#define list_for_each_entry(obj, head, member) \
    for (obj = container_of((head)->next, typeof(*obj), member); \
         &obj->member != (head); \
         obj = container_of(obj->member.next, typeof(*obj), member))

汎用連結リストの実装

#include 
#include 
#include 
#include 

// リストの走査
#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

// 構造体メンバmemberが構造体内の最初のメンバアドレスからのオフセット値を計算
#define offset(type, member) \
    ((size_t)&(((type*)0)->member))

// 構造体メンバのポインタから構造体変数の先頭アドレスを計算
#define container_of(ptr, type, member) \
    ((type*)((char*)(ptr) - offsetof(type, member)))

// 構造体ポインタobj、連結リストの先頭ノードhead、構造体メンバ名member
#define list_for_each_entry(obj, head, member) \
    for (obj = container_of((head)->next, typeof(*obj), member); \
         &obj->member != (head); \
         obj = container_of(obj->member.next, typeof(*obj), member))

typedef struct ListNode {
    struct ListNode *prev;
    struct ListNode *next;
} ListNode;

// ノードの作成
ListNode *create_node(void)
{
    ListNode *node = malloc(sizeof(ListNode));
    if (node) {
        node->prev = node;
        node->next = node;
    }
    return node;
}

// リストへのノード追加(内部関数)
void _add_node(ListNode *prev, ListNode *next, ListNode *new_node)
{
    next->prev = new_node;
    new_node->next = next;
    new_node->prev = prev;
    prev->next = new_node;
}

// 先頭にノードを追加
void add_node_head(ListNode *head, ListNode *node)
{
    _add_node(head, head->next, node);
}

// 末尾にノードを追加
void add_node_tail(ListNode *head, ListNode *node)
{
    _add_node(head->prev, head, node);
}

// リストが空か判定
bool is_list_empty(ListNode *head)
{
    return head->next == head;
}

// ノードの削除
void _remove_node(ListNode *node)
{
    node->prev->next = node->next;
    node->next->prev = node->prev;
    node->next = NULL;
    node->prev = NULL;
}

// 先頭ノードの削除
ListNode *remove_node_head(ListNode *head)
{
    if (is_list_empty(head)) return NULL;
    ListNode *node = head->next;
    _remove_node(node);
    return node;
}

// 末尾ノードの削除
ListNode *remove_node_tail(ListNode *head)
{
    if (is_list_empty(head)) return NULL;
    ListNode *node = head->prev;
    _remove_node(node);
    return node;
}

// 指定位置のノードを削除
ListNode *remove_node_at_index(ListNode *head, size_t index)
{
    ListNode *node = head->next;
    for (size_t i = 0; i < index; i++) {
        node = node->next;
        if (node == head) return NULL;
    }
    _remove_node(node);
    return node;
}

// サンプルデータ構造
typedef struct Employee {
    ListNode node;
    char department[30];
    char name[30];
    char gender;
    unsigned short age;
    double salary;
} Employee;

int main(int argc, const char *argv[])
{
    ListNode *head = create_node();
    if (!head) {
        fprintf(stderr, "Failed to create head node\n");
        return 1;
    }

    // 従業員データの追加
    for (int i = 0; i < 10; i++) {
        Employee *emp = malloc(sizeof(Employee));
        if (!emp) {
            fprintf(stderr, "Failed to allocate employee\n");
            continue;
        }
        
        snprintf(emp->department, sizeof(emp->department), "Development");
        snprintf(emp->name, sizeof(emp->name), "Tanaka%d", i);
        emp->gender = i % 2 ? 'F' : 'M';
        emp->age = 25 + (i % 10);
        emp->salary = 200000 + (i * 10000);
        
        add_node_tail(head, &emp->node);
    }

    // リストの走査と表示
    ListNode *pos = NULL;
    list_for_each(pos, head) {
        Employee *emp = container_of(pos, Employee, node);
        printf("%s %s %c %u %.0f\n", 
               emp->department, emp->name, emp->gender, 
               emp->age, emp->salary);
    }

    // メモリ解放(省略)
    return 0;
}

タグ: データ構造 C言語 連結リスト ポインタ メモリ管理

7月18日 00:49 投稿