2010-01-26 9 views
6

Jaki jest najlepszy sposób na wyzerowanie nowej pamięci po wywołaniu realloc przy zachowaniu pierwotnie przydzielonej pamięci w stanie nienaruszonym?Jak wyzerować nową pamięć po ponownym przydzieleniu

#include <stdlib.h> 
#include <assert.h> 
#include <string.h> 
#include <stdio.h> 

size_t COLORCOUNT = 4; 

typedef struct rgb_t { 
    int r; 
    int g; 
    int b; 
} rgb_t; 

rgb_t** colors; 

void addColor(size_t i, int r, int g, int b) { 
    rgb_t* color; 
    if (i >= COLORCOUNT) { 
     // new memory wont be NULL 
     colors = realloc(colors, sizeof(rgb_t*) * i); 
     //something messy like this... 
     //memset(colors[COLORCOUNT-1],0 ,sizeof(rgb_t*) * (i - COLORCOUNT - 1)); 

     // ...or just do this (EDIT) 
     for (j=COLORCOUNT; j<i; j++) { 
      colors[j] = NULL; 
     } 

     COLORCOUNT = i; 
    } 

    color = malloc(sizeof(rgb_t)); 
    color->r = r; 
    color->g = g; 
    color->b = b; 

    colors[i] = color; 
} 

void freeColors() { 
    size_t i; 
    for (i=0; i<COLORCOUNT; i++) { 
     printf("%x\n", colors[i]); 
     // can't do this if memory isn't NULL 
     // if (colors[i]) 
     // free(colors[i]); 

    } 
} 


int main() { 
    colors = malloc(sizeof(rgb_t*) * COLORCOUNT); 
    memset(colors,0,sizeof(rgb_t*) * COLORCOUNT); 
    addColor(0, 255, 0, 0); 
    addColor(3, 255, 255, 0); 
    addColor(7, 0, 255, 0); 


    freeColors(); 
    getchar(); 
} 
+0

Ma to * bardzo * słabą wydajność, gdy jeden kolor zostanie dodany na końcu listy kolorów, jak zwykle we wzorcu wywołania. Dodajesz tylko jeden element na raz przed ponownym przydzieleniem. Rozważ co najmniej przydzielenie maksimum (i + 1, COLORCOUNT * 2). –

+0

To tylko przykład ilustrujący problem. Rzeczywistym źródłem jest tablica haszująca, której rozmiar zmienia się według numeru głównego IIRC –

Odpowiedz

4

Prawdopodobnie nie ma potrzeby, aby zrobić memset: nie może być używany colors[k] przed ustawieniem go z czegoś ważnego później. Na przykład twój kod ustawia colors[i] na nowo przydzielony wskaźnik color, więc nie musisz ustawiać colors[i] na NULL.

Ale nawet jeśli chcesz "wyzerować, więc wszystko jest miłe", lub naprawdę potrzebujesz nowych wskaźników, aby być NULL: standard C nie gwarantuje, że wszystkie bity-zero jest stałą wskaźnika zerowego (tj. NULL), więc memset() nie jest właściwym rozwiązaniem.

Jedyny przenośny, co można zrobić, to ustawić każdy wskaźnik NULL w pętli:

size_t k; 
for (k=COLORCOUNT; k < i+1; ++k) /* see below for why i+1 */ 
    colors[k] = NULL; 

Twoim głównym problemem jest to, że rozmowa realloc() jest źle. realloc() zwraca wskaźnik do pamięci ze zmienioną wielkością, nie musi (koniecznie) zmieniać jej rozmiaru w miejscu.

więc należy zrobić:

/* i+1 because you later assign to colors[i] */ 
rgb_t **tmp = realloc(colors, (i+1) * sizeof *tmp); 
if (tmp != NULL) { 
    /* realloc succeeded, can't use colors anymore */ 
    colors = tmp; 
} else { 
    /* realloc failed, colors is still valid */ 
} 

Jeśli naprawdę chcesz wiedzieć, co wywołanie memset() powinno być, trzeba ustawić na zero pamięci zaczynając od colors+COLORCOUNT i ustaw i+1-COLORCOUNT członków do zera:

memset(colors+COLORCOUNT, 0, (i+1-COLORCOUNT) * sizeof *colors); 

Ale jak powiedziałem powyżej, wszystkie bajty zerowe nie jest gwarantowana być NULL wskaźnik, więc memset() jest bezużyteczne i tak. Musisz użyć pętli, jeśli chcesz mieć wskaźniki NULL.

+0

Dzięki - błąd realloc był niedopatrzeniem podczas szybkiego zapisywania testu, ale nie zdawałem sobie sprawy, że wszystkie bajty zerowe nie gwarantują NULL wszystkich elementów tablicy zgodnie z normą. –

3

Po pierwsze, ponowne wysłanie może się nie powieść, więc musisz sprawdzić NULL. Po drugie, nie ma lepszego sposobu na wyzerowanie pamięci: po prostu odstawienie z końca starego bufora na koniec większego bufora.

+0

Moja skomentowana sekcja powoduje zawieszenie visual studio z "msvcr90d.dll! Memset (unsigned char * dst = 0x00000000, unsigned char value = '', unsigned long count = 2553828) Linia 103 \t Asm ". Czy jestem tuż przy 1? –

+1

Wygląda na to, że przekazujesz wartość w kolorach [COLORCOUNT-1] do ponownego przydzielenia, a nie wskaźnik do tej lokalizacji. – atk

+0

realloc może nie być w stanie ponownie przydzielić miejsca i zwróci nowy wskaźnik. Powinieneś to sprawdzić i zaktualizować kolor za pomocą nowej wartości, jeśli nie ma ona wartości NULL. To jeden z powodów, dla których kod może się nie udać. Innym powodem może być to, że realloc nie może przydzielić więcej pamięci (zwrócił NULL, aby ci to powiedzieć, ale ty go odrzuciłeś). – Draemon

1

Napisz własną funkcję, powiedzmy reallocz, który akceptuje aktualną wielkość jako parametr i wywołuje realloc i memset dla Ciebie. To naprawdę nie będzie dużo lepsze niż to, co już masz ... w końcu to C.

6

Nie ma sposobu, aby rozwiązać to jako ogólny wzór. Powodem jest to, że aby wiedzieć, jaka część bufora jest nowa, musisz wiedzieć, jak długo był stary bufor. Nie jest możliwe określenie tego w C, a tym samym uniemożliwia ogólne rozwiązanie.

Jednakże można napisać otoki Podobnie jak

void* realloc_zero(void* pBuffer, size_t oldSize, size_t newSize) { 
    void* pNew = realloc(pBuffer, newSize); 
    if (newSize > oldSize && pNew) { 
    size_t diff = newSize - oldSize; 
    void* pStart = ((char*)pNew) + oldSize; 
    memset(pStart, 0, diff); 
    } 
    return pNew; 
} 
Powiązane problemy