Можем ли да използваме двойно свързан списък в C без динамично разпределение на паметта?

Опитвам се да използвам структура от данни с двойно свързан списък, за да приложа политика за заместване в мениджър на буфери.

Но моята C програма няма библиотека със свързани списъци, така че просто дефинирах структурата на данните сам.

Проблемът е: Мога ли да избегна каквото и да е [динамично?] разпределение на паметта, за да използвам двойно свързан списък?

Какво е предимството да използвате palloc() вместо malloc()?


person runcode    schedule 20.10.2012    source източник
comment
Много е трудно да дешифрирате това, което питате, поради лоша граматика. Двойно свързан свързан списък е структура от данни, което предполага, че съдържа данни и тези данни трябва да се намират в някаква разпределена памет, но тази памет може да бъде разпределена по всякакъв начин, така че malloc не е необходим. Съмнявам се обаче, че това отговаря на въпроса ви, тъй като нямам представа какво е...   -  person Andreas Vinter-Hviid    schedule 20.10.2012
comment
@RavindraBagale неговия базиран на пул за съхранение разпределител, а не стандартен (поне не, за който знам).   -  person WhozCraig    schedule 20.10.2012
comment
Това е свързан списък, така че трябва да е динамичен .. но ако не разпределяте паметта динамично, тогава как ще съберете толкова данни, колкото искате?   -  person Omkant    schedule 20.10.2012


Отговори (1)


Със сигурност можете да създадете двойно свързан списък, без изобщо да използвате динамично разпределение на паметта; просто е конвенционално да се прави това.

#include <stdio.h>
#include <assert.h>
#include <inttypes.h>

enum { NODEBUG = 0, DEBUG = 1 };

static const int debug = NODEBUG;

typedef struct DList DList;
struct DList
{
    int    data;
    DList *next;
    DList *prev;
};

enum { MAX_DLIST = 100 };
struct DList dlist[MAX_DLIST];

static void add_node(DList *head, DList *node)
{
    assert(head != 0 && node != 0);
    if (head->next == 0)
    {
        assert(head->prev == 0);
        head->next = node;
        head->prev = node;
        node->next = head;
        node->prev = head;
    }
    else
    {
        assert(head->prev != 0);
        node->next = head->next;
        node->prev = head;
        head->next->prev = node;
        head->next = node;
    }
}

static void diagnode(DList *node)
{
    if (debug)
        printf(" (T = 0x%.12" PRIXPTR ", N = 0x%.12" PRIXPTR ", P = 0x%.12" PRIXPTR ")\n",
               (uintptr_t)node, (uintptr_t)node->next, (uintptr_t)node->prev);
}

static void print_list(DList *head)
{
    assert(head != 0);
    printf("List:");
    if (head->next != 0)
    {
        DList *node;
        int counter = 0;
        if (debug)
            printf("\nHEAD");
        diagnode(head);
        for (node = head->next; node != head; node = node->next)
        {
            printf(" %3d", node->data);
            diagnode(node);
            assert(counter++ < MAX_DLIST);
        }
    }
    printf(" <EOL>\n");
}

int main(void)
{
    DList head = { 0, 0, 0 };

    for (int i = 0; i < MAX_DLIST; i++)
    {
        dlist[i].data = (i * 13 + 7) % 100;
        add_node(&head, &dlist[i]);
        if (debug)
            print_list(&head);
    }
    print_list(&head);
}

Не се вижда разпределение на памет! Можете да използвате вариант на това, когато имате нещо като мениджър на буфери, където има фиксиран масив от буфери за данни, но искате LRU (най-малко използвани) политика за замяна. Вместо данните да са директно в структурата на двойно свързания списък, както в този пример, елементът data ще сочи към запис в буферния пул. След това можете да добавяте и премахвате записи от списъка, без да променяте нищо в основната структура от данни, към която е свързан вашият списък.


Ако palloc() е разпределител на пул памет, предимството да го използвате пред malloc() е, че можете да освободите цялата памет, разпределена за даден пул с едно извикване на функция, вместо да се налага сами да управлявате всички отделни освобождавания. Понякога разпределителят на пул ще бъде по-ефективен от отделното разпределение на памет с malloc(); може да разпредели голям масив от структура с фиксиран размер и след това да раздаде записи при поискване, намалявайки броя на разпределенията и по този начин намалявайки размера на режийните разходи.

person Jonathan Leffler    schedule 20.10.2012
comment
благодаря ви за отговора, нямам представа защо други хора ми дават отрицателни оценки.. това глупав въпрос ли е? - person runcode; 21.10.2012
comment
Това не е глупав въпрос, въпреки че е сравнително необичайно да не се прави динамично разпределение на памет със свързани списъци. Мениджърът на буфери обаче е едно от местата, където избягването на режийните разходи за повтарящо се разпределение на единичен елемент чрез разпределяне на масив (статично, както в моя отговор, или динамично разпределяне на масива наведнъж) и използването на това е напълно разумно. Подозирам, че гласувалите против не са се сблъсквали с подобни ситуации и техният предишен опит с „прочетете списък със стойности от стандартен вход“ не им е показал алтернативи, така че те не са осъзнали какво правят... - person Jonathan Leffler; 21.10.2012
comment
Можеше да си спестиш малко мъка, като споменеш какво е palloc(); разумно е да се очаква познаване на стандартната библиотека C89 и най-вече разумно да се приеме, че и C99. Преминете към езотеричните нови части на C2011 и трябва да споменете това; преместете в POSIX или Windows API и трябва поне да укажете това. Преместете се много извън тях и трябва да посочите къде хората могат да намерят информация за това какво е функция и какво прави. Не съм търсил в Google palloc() преди, но има различни версии; Надявам се да направят горе-долу същото. - person Jonathan Leffler; 21.10.2012