Нужен совет по реализации malloc и бесплатно в C

Для школы мне нужно написать программу, которая использует мою собственную реализацию malloc и free. Мне нужно иметь возможность сообщать обо всех фрагментах памяти в моей «куче», независимо от того, выделена она или нет. Я чувствую, что написал хороший код для этого, но, очевидно, это не так. Первые несколько раз, когда я его запускал, отчет постоянно сообщал об одном и том же адресе. При попытке отладить это наступил момент, когда программа даже не позволяла мне начать выделять пространство для использования в качестве моей «кучи», она просто получала ошибку сегментации и закрывалась. Любые указатели на то, где я ошибаюсь, или даже на очистку моего кода, были бы очень полезны.

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


#define WORDSIZE 8
#define ALLOCMAGIC 0xbaddecaf
#define FREEMAGIC 0xdeadbeef

typedef struct __header_t {
    size_t size;
    int magic;
} header_t;

typedef struct __node_t {
    size_t size;
    struct __node_t *next;
} node_t;

node_t *head = NULL;


// Find the free node that occurs just before the given node.
node_t *findLastFree(node_t * node) {

    // Initialize some pointers to traverse the free node linked list;
    node_t *lastFree = head;
    node_t *nextFree = lastFree->next;

    // Traverse linked list until the last node's pointer is pointed to NULL,
    // meaning the end of the list.
    while (nextFree != NULL) {

        // Check if target node is less than the next node, meaning the target node
        // is between last and next.  If so, then return last node.
        if (node < nextFree) {
            return lastFree;
        }

        lastFree = nextFree;
        nextFree = lastFree->next;
    }

    // If we have reached the end of the list and the target node is still greater
    // than the last node, return last node.
    return lastFree;
}


// If the given pointer is allocated, deallocate the space and coalesce the free
// node list.
void myFree(void *ptr) {

    // Initialize some free node pointers and assert that the given pointer is
    // the beginning of allocated space.
    node_t *lastFree;
    node_t *nextFree;
    node_t *newFree;
    header_t *block = ((header_t *) ptr) - 1;
    assert(block->magic == ALLOCMAGIC);

    // Set this block's signal to free space
    block->magic = FREEMAGIC;

    // Typecast the block into a free node and set it's size.
    size_t size = block->size + sizeof(header_t);
    newFree = (node_t *) block;
    newFree->size = size;

    // Check if node is before the first free node.  If so set the new node as
    // the new head.  If not, then handle node as it occurs after head.
    if (newFree < head) {

        nextFree = head;

        // Check if new node ends at the start of head.  If so, merge them
        // into a single free node.  Else point the new node at the previous head.
        // Either way, set new free as the new head.
        if ((newFree + newFree->size) == head) {
            newFree->next = head->next;
            newFree->size = newFree->size + head->size;
        } else {
            newFree->next = head;
        }
        head = newFree;
    } else {

        // Set the free nodes for before and after the new free node.
        lastFree = findLastFree(newFree);
        nextFree = lastFree->next;

        // Check if new node is the last node.  If so, point the previous final
        // node at the new node and point the new node at NULL.
        if (nextFree == NULL) {

            lastFree->next = newFree;
            newFree->next = NULL;
        }

        // Check if end of new node is touching next node.  If so, merge them
        // into a single free node.  Else point new free and next free.
        if ((newFree + newFree->size) == nextFree) {
            newFree->next = nextFree->next;
            newFree->size = newFree->size + nextFree->size;
        } else {
            newFree->next = nextFree;
        }

        // Check if start of new node is touching last free node.  If so, merge
        // them into a single free node.  Else point last's next to new free.
        if ((lastFree + lastFree->size) == newFree) {
            lastFree->next = newFree->next;
            lastFree->size = lastFree->size + newFree->size;
        } else {
            lastFree->next = newFree;
        }
    }
}


// Split the given free node to fit the given size.  Create a new node at the 
// remainder and rearrange the free list to accomodate.
void splitBlock(node_t *node, size_t size) {

    // Create a new pointer at the end of the requested space.
    void *newBlock = node + size;

    // Set the bits of the new space as if it were allocated then freed.
    header_t *hptr = (header_t *) newBlock;
    hptr->size = (node->size - size - sizeof(header_t));
    hptr->magic = FREEMAGIC;

    // Typecast the new space into a node pointer.  Reinsert it into the free
    // node list.
    node_t *newFree = (node_t *) newBlock;
    newFree->size = node->size - size;
    newFree->next = node->next;
    node_t *lastFree = findLastFree(newFree);
    lastFree->next = newFree;
}


// Find a free node that can fit the given size.  Split the node so no space is 
// wasted.  If no node can fit requested size, increase the heap size to accomodate.
void *findFirstFit(size_t size) {

    // Create a node pointer to traverse the free node list.
    node_t *node = head;

    // Traverse the list until the end is reached.
    while(node != NULL) {

        // Check if the node can accomodate the requested size.
        if (node->size >= size) {

            // Split the current node at the requested size and return a pointer
            // to the start of the requested space.
            splitBlock(node, size);
            return (void *) node;
        }
        node = node->next;
    }

    // No free space could fit requested size, so request more space at the end
    // of the heap.
    void *newspace = sbrk(size);
    assert(newspace >= 0);

    return newspace;
}


// Allocate a block of space for the given size and return a pointer to the start
// of the freed space.

void *myMalloc(size_t need) {

    // Round the given size up to the next word size.  Add the size of a header to
    // the amount actually needed to allocate.
    need = (need + WORDSIZE - 1) & ~(WORDSIZE - 1);
    size_t actual = need + sizeof(header_t);

    // Find a free node that can accomodate the given size.  Check it is valid.
    void *firstfit = findFirstFit(actual);
    assert(firstfit >= 0);

    // Create a header for the newly allocated space.
    header_t *hptr = (header_t *) firstfit;
    hptr->magic = ALLOCMAGIC;
    hptr->size = need;

    return (void *) (hptr + 1);
}


// Print a report on the space starting at the given pointer.  Return a pointer to
// the start of the next block of space.
void *reportAndGetNext(void *ptr) {

    void *nextptr;
    header_t *hptr = (header_t *) ptr;

    // Check if the pointer is pointing to allocated space.
    if (hptr->magic == ALLOCMAGIC) {

        // Report the characteristics of the current block.
        printf("%p is ALLOCATED starting at %p and is %zd bytes long.\n", hptr, (hptr + 1), hptr->size);

        // Set the next pointer to be returned.
        nextptr = hptr + hptr->size + sizeof(header_t);
    } else {

        // Cast the pointer as a free node.  Set the next pointer to be returned.
        node_t *free = (node_t *) ptr;
        nextptr = free + free->size;

        // Report the characteristics of the current block.
        printf("%p is FREE for %zd bytes.\n", hptr, free->size);
    }
    return nextptr;
}


// Report on all blocks of space contained within the heap space, starting at the 
// given pointer.
void report(void* startheap) {

    void *ptr = startheap;
    void *end = sbrk(0);
    int count = 50;

    printf("Current Status of Heap:\n");

    while (ptr != NULL && count > 0) {
        ptr = reportAndGetNext(ptr);
        count = count - 1;
    }

    printf("Heap Length: %zd \n", (end - startheap));

}


int main(void) {

    void *start = sbrk(4096);
    assert(start >= 0);
    head = (node_t *) start;
    head->size = 4096;
    head->next = NULL;

    printf("Allocating block 1");
    void *ptr1 = myMalloc(26);
    void *ptr2 = myMalloc(126);
    report(start);
    myFree(ptr1);
    myFree(ptr2);

    return 0;
}

person ScottyD    schedule 11.10.2018    source источник
comment
Обратите внимание, что вы не должны создавать имена функций, переменных, тегов или макросов, которые начинаются с подчеркивания. C11 §7.1.3 Зарезервированные идентификаторы говорит (частично ): — Все идентификаторы, начинающиеся с символа подчеркивания и либо с прописной буквы, либо с другого символа подчеркивания, всегда зарезервированы для любого использования. — Все идентификаторы, начинающиеся с подчеркивания, всегда зарезервированы для использования в качестве идентификаторов с областью действия файла как в обычном пространстве имен, так и в пространстве имен тегов. См. также Что означает двойное подчеркивание (__const) в C?   -  person Jonathan Leffler    schedule 12.10.2018
comment
@JonathanLeffler: Если цель состоит в том, чтобы написать библиотеку, позволяющую использовать автономную реализацию в качестве реализации хостинга для запуска других программ, то сама библиотека по существу станет частью реализации, используемой те другие программы. Частные объявления библиотек, которые должны быть включены в заголовки библиотек (например, чтобы макросы работали), которые не начинаются с символов подчеркивания, могут столкнуться с именами, используемыми в клиентском коде.   -  person supercat    schedule 12.10.2018
comment
вопрос № 1: вы пытались отладить его с помощью отладчика?   -  person pm100    schedule 12.10.2018
comment
@supercat: комментарий «в целом» предназначен для таких исключений. Если вы знаете достаточно, чтобы спорить, вы можете сами решить, использовать ли имена, зарезервированные для реализации. Если вы не знаете, то избегайте имен, начинающихся с подчеркивания, это простой и эффективный совет (или избегайте имен, начинающихся с двойного подчеркивания или подчеркивания и заглавной буквы).   -  person Jonathan Leffler    schedule 12.10.2018
comment
@JonathanLeffler: Достаточно честно. Я подумал, что стоит упомянуть, что создание кода библиотеки является исключением из этого принципа, поскольку это то, что делает OP.   -  person supercat    schedule 12.10.2018
comment
@ScottyD, используя -fsanitize=address и -g, показывает мне, что ваш segfault находится в строке 154, поэтому я думаю, что в доступе node->size.   -  person nullp0tr    schedule 12.10.2018
comment
Выглядит хорошо (лень отлаживать) Подсказка: используйте for()loops. Просто для здравомыслия. Плюс: ваша функция void myFree(void *ptr) {} может быть слишком большой для управления.   -  person wildplasser    schedule 12.10.2018
comment
@ nullp0tr Вы правы, именно в этой строке происходит ошибка seg. Любопытно, однако, что я вставил операторы печати, чтобы лучше понять, с чем я имею дело. Я могу использовать myMalloc один раз, а при втором использовании возникает ошибка сегментации. Но у меня есть оператор печати прямо перед if (node->size >= size), где я печатаю node->size. Без проблем. Так что я понятия не имею, откуда берется ошибка.   -  person ScottyD    schedule 12.10.2018
comment
Вы можете извлечь пользу из неуклюже написанного, но хорошего базового введения Учебное пособие по Malloc Обратите особое внимание, если вы внедряете 64-разрядную систему, поскольку руководство было написано для 32-разрядной реализации (вы можете относительно легко адаптироваться к 64-разрядной версии)   -  person David C. Rankin    schedule 12.10.2018


Ответы (1)


Первая очевидная ошибка, которую я вижу, связана с арифметикой указателя. SplitBlock пытается разделить size байт из начала блока, но когда вы это делаете:

void splitBlock(node_t *node, size_t size) {
    // Create a new pointer at the end of the requested space.
    void *newBlock = node + size;

Ваш указатель newBlock на самом деле находится в size * sizof(node_t) байтах в блоке, что вполне может быть за концом блока. Вам нужно привести node к char *, прежде чем выполнять с ним арифметику указателя, если вам нужны смещения байтов. Тем не менее, вы можете столкнуться с проблемами выравнивания...

person Chris Dodd    schedule 12.10.2018