2

非常に大きなデータセットでのメモリの断片化を避けるために、二重にリンクされたリストを実装して、malloc2 回呼び出すことを回避mallocしました。代わりに、およびノードを含む のオフセットを取得するために、必要なスペースを 1 回で割り当てます。prevnextalignofstructprevnext

実装はここにありますが、関連する部分を抽出しています:

#include <stdlib.h>
#include <stdint.h>
#include <stdalign.h>

struct node
{
    struct node *prev;
    struct node *next;
};

typedef struct
{
    struct node *head;
    struct node *tail;
    size_t offset;
    size_t size;
} klist;

klist *klist_create(size_t size)
{
    klist *list = calloc(1, sizeof *list);

    if (list != NULL)
    {
        size_t align = alignof(struct node);

        // Round size up to nearest multiple of alignof(struct node)
        list->offset = (size + (align - 1)) / align * align;
    }
    return list;
}

#define klist_node(list, data) ((void *)((uintptr_t)(const void *)data + list->offset))
#define klist_data(list, node) ((void *)((uintptr_t)(const void *)node - list->offset))

void *klist_push_head(klist *list)
{
    void *data = calloc(1, list->offset + sizeof(struct node));

    if (data == NULL)
    {
        return NULL;
    }

    struct node *node = klist_node(list, data);

    if (list->head != NULL)
    {
        list->head->prev = node;
        node->next = list->head;
    }
    else
    {
        list->tail = node;
    }
    list->head = node;
    list->size++;
    return data;
}

void *klist_head(const klist *list)
{
    if (list->head != NULL)
    {
        return klist_data(list, list->head);
    }
    return NULL;
}

...

次に、でmain

struct data
{
    int key;
    char *value;
};

klist *list = klist_create(sizeof(struct data));
struct data *data = klist_push_head(list);

data->key = 1;
data->value = "one";

wheredataは、任意のプリミティブ型または複合型へのポインターにすることができます。

問題は、関連するすべてのメンバーを含む典型的なパッケージ構造ではないということです。

struct node
{
    void *data;
    struct node *prev;
    struct node *next;
};

有効な型のルールが気になります:

文字型ではない型を持つ左辺値を介して、宣言された型を持たないオブジェクトに値が格納された場合、その左辺値の型は、そのアクセスおよびその後のオブジェクトを変更しないアクセスの有効な型になります。保存された値。

この規則はリストの実装にどのように影響しますか?

合法/移植可能なコードですか?

4

1 に答える 1