2010-01-26 8 views
6

¿Cuál es la mejor manera de poner a cero la memoria nueva después de llamar a realloc manteniendo intacta la memoria inicialmente asignada?Cómo poner a cero la memoria nueva después de realloc

#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

Esto tiene * muy * bajo rendimiento cuando se agrega un color al final de la lista de colores, el patrón de llamada habitual. Solo agregará un elemento a la vez antes de reasignarlo. Considere al menos asignar max (i + 1, COLORCOUNT * 2). –

+0

Esto es solo un ejemplo para ilustrar el problema. La fuente real es una tabla hash que cambia de tamaño según el número primo IIRC –

Respuesta

4

Probablemente no hay necesidad de hacer el memset: no se puede estar utilizando colors[k] antes de dejarla con algo válido después. Por ejemplo, su código establece colors[i] en un puntero recién asignado color, por lo que no necesitó establecer colors[i] en NULL.

Pero, incluso si quería "ponerlo a cero para que todo esté bien", o realmente necesita los nuevos punteros para ser NULL: el estándar C no garantiza que todos los bits cero sea la constante del puntero nulo (es decir, NULL), por lo que memset() no es la solución correcta de todos modos.

Lo único portátil que puede hacer es fijar cada puntero a NULL en un bucle:

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

Su principal problema es que su llamada realloc() está mal. realloc() devuelve un puntero a la memoria redimensionada, no (necesariamente) redimensiona en su lugar.

lo tanto, usted debe hacer:

/* 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 */ 
} 

Si realmente quiere saber lo que la llamada memset() debe ser, es necesario establecer a cero la memoria que comienza en colors+COLORCOUNT, y establecer i+1-COLORCOUNT miembros a cero:

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

Pero como he dicho anteriormente, todos los bytes cero no se garantiza que sea un puntero NULL, por lo que su memset() es inútil de todos modos. Tienes que usar un bucle si quieres NULL punteros.

+0

Gracias - el error de realloc fue un descuido mientras escribía rápidamente la prueba, pero no me di cuenta de que todos los bytes cero no garantizan NULL a todos los miembros de la matriz según el estándar. –

3

En primer lugar, realloc puede fallar, por lo que debe comprobar NULL. En segundo lugar, no hay mejor manera de poner a cero la memoria: solo memset desde el final del antiguo buffer hasta el final del buffer más grande.

+0

mi sección comentada bloquea el visual studio con "msvcr90d.dll! Memset (unsigned char * dst = 0x00000000, unsigned char value = '', unsigned long count = 2553828) Line 103 \t Asm ". ¿Acabo de salir por 1? –

+1

Parece que está transfiriendo el valor en los colores [COLORCOUNT-1] para reasignar, en lugar de un puntero a esa ubicación. – atk

+0

realloc puede no ser capaz de reasignar en el lugar y devolverá un nuevo puntero. Debería verificar esto y actualizar el color con el nuevo valor si no es NULL. Esa es una razón por la cual el código puede fallar. La otra razón puede ser que realloc no pudo asignar más memoria (devolvió NULL para decirte esto, pero la descartas). – Draemon

1

Escriba su propia función, digamos reallocz, que acepte el tamaño actual como parámetro, y llame a realloc y memset por usted. Realmente no será mucho mejor de lo que ya tienes ... es C, después de todo.

6

No hay forma de resolver esto como un patrón general. La razón es que para saber qué parte del búfer es nueva necesita saber cuánto tiempo estuvo el búfer antiguo. No es posible determinar esto en C y, por lo tanto, evita una solución general.

Sin embargo, usted podría escribir un envoltorio al igual que

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; 
} 
Cuestiones relacionadas