2008-10-19 9 views
6

Jak rozumiem, pytany zarezerwować większy blok pamięci, funkcja realloc() zrobi jedną z trzech rzeczy:Ustalenie realloc() zachowanie przed wywołaniem to

 
if free contiguous block exists 
    grow current block 
else if sufficient memory 
    allocate new memory 
    copy old memory to new 
    free old memory 
else 
    return null 

hodując obecny blok jest bardzo tanią operacją, więc takie zachowanie chciałbym wykorzystać. Jeśli jednak ponownie przydzielam pamięć, ponieważ chcę (na przykład) wstawić znak na początku istniejącego łańcucha, nie chcę, aby funkcja realloc() kopiowała pamięć. Kończę kopiowanie całego łańcucha za pomocą realloc(), a następnie skopiuję go ponownie ręcznie, aby zwolnić pierwszy element tablicy.

Czy można określić, co zrobi realloc()? Jeśli tak, czy można to osiągnąć w sposób międzyplatformowy?

+1

Ostatnie z 3 przypadków jest niepoprawne, realloc zwróci wskaźnik zerowy, jeśli nie ma wystarczającej ilości pamięci, a nie wskazywanego wskaźnika. –

+0

Jak powiedział Robert Gamble - realloc() zwraca NULL na brak pamięci. –

+0

Dzięki temu poprawiono pseudokod. – Ant

Odpowiedz

7

realloc() „s zachowanie jest prawdopodobne w zależności od jego konkretnego wdrożenia. Na podstawie tego kodu byłby straszny hack, który, co najmniej, narusza hermetyzację.

Lepszym rozwiązaniem dla konkretnego przykładu jest:

  1. Znajdź rozmiar bufora
    • przydzielić nowy bufor (z malloc()), większą niż poprzednia
    • Kopiowanie przedrostek, który chcesz dodać do nowego bufora
    • Skopiuj ciąg w poprzednim buforze do nowego bufora, zaczynając od prefiksu
    • Zwolnij poprzednią bufor
+0

To jest po prostu nielogiczne, dlaczego nie używać realloc i uzyskać poprawę wydajności przynajmniej w niektórych przypadkach Twoja sugestia zawsze będzie pamiętać. On chce poprawić wydajność i uniknąć memcpy, gdy nie są potrzebne. – Ilya

+0

To jest właściwie rozwiązanie, które obecnie mam wdrożone (chociaż generuję za każdym razem, aby zmniejszyć liczbę przyszłych przydziałów). Jak mówi Ilya, szukałem bardziej optymalnego rozwiązania, ale jak już zauważyłeś, prawdopodobnie już miałem najlepszy kompromis. – Ant

+0

Jeśli chcesz wstawić de char na początku, będziesz musiał użyć memmov() po ponownym przydzieleniu bufora ... Jeśli realloc() nie może wyhodować bieżącego bloku, skopiujesz go dwukrotnie, raz realloc(), raz memmov() Rozwiązanie Romulo zawsze kopiuje pamięć, ale tylko raz ...;) – alcuadrado

0

Nie - a jeśli o tym pomyślisz, to nie będzie działać. Pomiędzy sprawdzaniem, co zamierzasz zrobić, a faktycznym robieniem tego, inny proces może przydzielić pamięć. W aplikacji wielowątkowej nie może to działać. Pomiędzy sprawdzaniem, co ma zamiar zrobić, a faktycznym robieniem tego, inny wątek może przydzielić pamięć.

Jeśli obawiasz się tego typu sytuacji, być może nadszedł czas, aby spojrzeć na struktury danych, których używasz, aby sprawdzić, czy możesz rozwiązać problem. W zależności od tego, jak zbudowane są te łańcuchy, możesz to zrobić całkiem sprawnie dzięki dobrze zaprojektowanemu buforowi.

+0

Inny proces przydzieli go w swojej przestrzeni adresowej, więc nie jest to istotne. – Ilya

+0

Wystarczy zastąpić "proces" przez "wątek". – Constantin

+0

Jeszcze lepszy punkt! – Draemon

0

Nie sądzę, że jest to możliwe w trybie cross-platform. Here jest kod dla ulibc realizacji, które mogą dać wskazówkę jak to zrobić NIP platformy zależną drogę, faktycznie lepiej znaleźć źródło glibc ale ten był na górze google :)

+0

Rozważ użycie http://tinyurl.com/ dla długich adresów URL. –

+0

Dzięki redagowałem link. – Ilya

+1

proszę nie :-) http://www.codinghorror.com/blog/archives/001276.html –

0

Jeśli obstacks są dobrze dopasowane do Twoich potrzeb alokacji pamięci, można użyć ich funkcjonalność fast growing. Przeszkody są funkcją glibc, ale są również dostępne w bibliotece libiberty, która jest dość przenośna.

2

Jak zaznaczono w komentarzach, przypadek 3 w pytaniu (brak pamięci) jest nieprawidłowy; realloc() zwróci wartość NULL, jeśli nie ma dostępnej pamięci [pytanie zostało teraz naprawione].

Steve McConnell w 'Code Complete' wskazuje, że jeśli zapiszesz wartość zwracaną z realloc() w jedynej kopii oryginalnego wskaźnika, gdy realloc() ulegnie awarii, właśnie wyciekłaś pamięć.To znaczy:

void *ptr = malloc(1024); 
... 
if ((ptr = realloc(ptr, 2048)) == 0) 
{ 
    /* Oops - cannot free original memory allocation any more! */ 
} 

Różne implementacje funkcji realloc() będą zachowywać się inaczej. Jedyną bezpieczną rzeczą, którą można założyć, jest to, że dane będą zawsze przenoszone - zawsze otrzymasz nowy adres, gdy ponownie załadujesz() pamięć.

Jak ktoś inny zwrócił uwagę, jeśli jesteś zaniepokojony tym, może czas spojrzeć na twoje algorytmy.

1

Czy przechowywanie Twojego ciągu wstecz pomoże?

W przeciwnym razie ... po prostu malloc() więcej miejsca niż potrzebujesz, a gdy zabraknie Ci miejsca, skopiuj do nowego bufora. Prostą techniką jest podwójne podwojenie przestrzeni za każdym razem; działa to całkiem dobrze, ponieważ im większy ciąg (tzn. tym więcej czasu zajmuje kopiowanie do nowego bufora), tym rzadziej musi wystąpić.

Za pomocą tej metody można również poprawnie uzasadnić ciąg znaków w buforze, dzięki czemu można łatwo dodawać znaki na początku.

0

Dlaczego nie zachować jakąś pustą przestrzeń buforową w lewo od napisu, tak:

char* buf = malloc(1024); 
char* start = buf + 1024 - 3; 
start[0]='t'; 
start[1]='o'; 
start[2]='\0'; 

Aby dodać „on” na początku swojego łańcucha, aby „na \ 0”:

start-=2; 
if(start < buf) 
    DO_MEMORY_STUFF(start, buf);//time to reallocate! 
start[0]='o'; 
start[1]='n'; 

W ten sposób nie będziesz musiał kopiować swojego bufora za każdym razem, gdy chcesz wstawić go na początku.

Jeśli musisz wstawiać zarówno na początku, jak i na końcu, wystarczy trochę miejsca na oba końce; Wstawki w środku nadal będą wymagały oczywiście przetasowania elementów.

0

Lepszym rozwiązaniem jest użycie połączonej listy. Każdemu z obiektów danych przydzielonych na stronie i przydzielić inną stronę i link do niej, albo z poprzedniej strony lub ze strony indeksu. W ten sposób wiesz, kiedy następny przydział się nie powiedzie i nigdy nie będziesz musiał kopiować pamięci.

Powiązane problemy