2009-10-31 10 views
5

Poniżej jest bardzo bardzo prosta wersja malloc() i wydaje się, że przydziela mi trochę miejsca, ale oprócz tego, że nie ma wolnego() i nie sprawdzam, czy "przekroczyłem przydzielone miejsce, jak mogę sprawdzić, czy kod jest poprawny?Alokacja pamięci w C

Jakieś oczywiste błędy, które uderzą mnie w "C" ekspertów?

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

#define MAX_MEMORY 1024 * 1024 * 2 /* 2MB of memory */ 

void *stack = NULL; /* pointer to available stack */ 
void * memoryAlloc(size) { 
    if (stack == NULL) 
     stack = sbrk(MAX_MEMORY); /* give us system memory */ 

    void *pointer; 
    pointer = (void *)stack + size; /* we always have space :) */ 
    stack += size; /* move in stack forward as space allocated */ 
    return pointer; 
} 
+0

Nie nazwałbym areny pamięci "stosem". Twoja obecna implementacja jest jak stos, tak, ale jeśli zamierzasz działać tak jak malloc, za darmo, to jest kupa. –

+0

Dziękuję wszystkim za wszystkie komentarze! – Radek

+0

Ten podzielnik jest początkiem czegoś, co nazywa się "przydziałem puli" - alokacje odbywają się tak, jak powyżej, a następnie cały blok jest natychmiast zwracany do systemu, gdy jednostka pracy korzystająca z puli zostanie ukończona. Pomaga w radzeniu sobie z wyciekami spowodowanymi koniecznością zarządzania każdą małą alokacją osobno. Apache wykorzystuje pule - przychodzi żądanie HTTP, pula jest skonfigurowana dla żądania, po zakończeniu żądania pula jest zwalniana. Nic, co zajmuje się tą sprawą, nie musi martwić się o zwalnianie dynamicznie przydzielanych obiektów. –

Odpowiedz

11

Oprócz podstawowych problemów Ned Batchelder wskazał, znacznie bardziej subtelny problemem jest to, że podzielnik ma powrócić adres, który jest właściwie wyrównany dla cokolwiek obiekt jest przydzielona. Na niektórych platformach (x86) może to nie mieć znaczenia, z wyjątkiem problemów związanych z wydajnością, ale na wielu platformach jest to kompletne rozwiązanie.

Musiałem również wykonać rzutowanie (char*), aby wykonać arytmetykę wskaźnika stack (nie można wykonywać arytmetyki wskaźników na typach void*).

Powinieneś umieścić parens wokół wyrażenia w makrze MAX_MEMORY. Nie sądzę, by istniały jakiekolwiek problemy pierwszeństwa, w które się wchodzisz bez nich, ponieważ wszyscy operatorzy o wysokim priorytecie niż mnożenie nie mieliby poprawnej składni. W przypadku makr jest to zawsze bezpieczniejsze niż przykro mi. (Istnieje co najmniej jeden wyjątek, w którym operator [] może wiązać tylko z wyrażeniem 2, a nie całe wyrażenie MAX_MEMORY, ale byłoby to bardzo dziwne, aby zobaczyć MAX_MEMORY[arrayname], nawet jeśli jest poprawny pod względem składni).

W gruncie rzeczy zrobiłabym to wyliczeniem.

Prawdopodobnie można zachować podzielnika prosty wracając blok pamięci, który jest właściwie wyrównany dla dowolnego podstawowego typu danych w systemie (może przyrównanie 8 bajtów):

/* Note: the following is untested     */ 
/*  it includes changes suggested by Batchelder */ 

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

enum { 
    kMaxMemory = 1024 * 1024 * 2, /* 2MB of memory */ 
    kAlignment = 8 
}; 

void *stack = NULL; /* pointer to available stack */ 
void * memoryAlloc(size_t size) { 
    void *pointer; 

    size = (size + kAlignment - 1) & ~(kAlignment - 1); /* round size up so allocations stay aligned */ 

    if (stack == NULL) 
    stack = sbrk(kMaxMemory); /* give us system memory */ 

    pointer = stack; /* we always have space :) */ 
    stack = (char*) stack + size; /* move in stack forward as space allocated */ 
    return pointer; 
} 
+0

Testowany i działający – Radek

6

Istnieje kilka problemów:

  1. zadeklarować pointer w środku funkcji, co jest niedozwolone w C

  2. ustawić wskaźnik do stack+size, ale chcesz to być tylko stack. W przeciwnym razie zwracasz wskaźnik do końca bloku pamięci, który przydzielasz. W rezultacie, jeśli Twój rozmówca użyje wszystkich size bajtów w tym wskaźniku, będzie nakładał się na inny blok pamięci. Jeśli otrzymasz bloki o różnej wielkości w różnych momentach, będziesz mieć dwóch programistów próbujących użyć tych samych bajtów pamięci.

  3. Kiedy robisz stack += size, jesteś zwiększając stack nie przez size bajtów ale size void * 's, który jest prawie zawsze większa.

+3

Deklaracje w środku funkcji są dozwolone od czasu C99. –

+0

oops: Pokazuję swój wiek! –

+0

wszystkie dobre punkty, +1 –

2

Po pierwsze, jako drugi już zauważyć , deklarujesz zmienne w środku bloku, co jest dozwolone tylko w C99, ale nie w C89/90. To znaczy. musimy wywnioskować, że używasz C99.

Po drugie, definiujesz swoją funkcję w K & R-style (bez parametru), ale jednocześnie nie deklarujesz później tego typu parametru. W ten sposób polegasz na zasadzie "implicit int", która jest zakazana w C99. To znaczy.musimy wywnioskować, że twoje są nie przy użyciu C99. Jest to już sprzeczne z częścią "po pierwsze". (Dodatkowo zwykle używa się typów niepodpisanych do reprezentowania pojęcia "rozmiaru obiektu" .size_t jest dedykowanym typem zwykle używanym do tego celu).

Po trzecie, używamy wskaźnika arytmetycznego na wskaźniku void *, który jest zawsze niezgodny z C89/90 i C99. Nie wiem nawet, co możemy z tego wyciągnąć :)

Proszę, zdecyduj, jakiego języka próbujesz użyć, a my pójdziemy stamtąd.

+0

Brakowało mi domyślnej deklaracji "rozmiaru". –

+0

Dzięki AndreyT, zamierzam użyć C89 i możesz wywnioskować, że jestem początkującym, ale z wieloma pomocnymi wpisami teraz :). – Radek