2008-10-19 6 views
6

lo que tengo entendido, cuando se le preguntó a reservar un bloque más grande de memoria, la función realloc() hará una de tres cosas diferentes:determinar el comportamiento realloc() antes de llamar a

 
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 

creciente del el bloque actual es una operación muy barata, por lo que este es un comportamiento que me gustaría aprovechar. Sin embargo, si estoy reasignando memoria porque quiero (por ejemplo) insertar una char al comienzo de una cadena existente, no quiero que realloc() copie la memoria. Terminaré copiando toda la cadena con realloc(), y luego la copiaré de nuevo manualmente para liberar el primer elemento de la matriz.

¿Es posible determinar qué hará realloc()? Si es así, ¿es posible lograrlo de forma cruzada?

+1

El último de sus 3 casos es incorrecto, realloc devolverá un puntero nulo si no hay memoria suficiente, no el puntero que lo pasó. –

+0

Como dijo Robert Gamble: realloc() devuelve NULL sin memoria. –

+0

Gracias ambos, pseudocódigo corregido. – Ant

Respuesta

7

realloc() 's es probable depende de su implementación específica. Y basar tu código en eso sería un truco terrible que, por decir lo menos, viola la encapsulación.

Una mejor solución para su ejemplo específico es:

  1. encontrar el tamaño del búfer en
    • asignar una nueva memoria intermedia (con malloc()), mayor que la anterior
    • Copiar el prefijo que desee para el nuevo almacenamiento intermedio
    • Copie la cadena en el almacenamiento intermedio anterior en el nuevo almacenamiento intermedio, comenzando después del prefijo
    • Soltar el tampón anterior
+0

Esto no es lógico, ¿por qué no utilizar Realloc y obtener la mejora del rendimiento al menos en algunos de los casos? , tu sugerencia será memcpy siempre. Él quiere mejorar el rendimiento y evitar memcpy cuando no sea necesario. – Ilya

+0

Esta es en realidad la solución que he implementado actualmente (aunque sobreasloco cada vez para reducir el número de reasignaciones futuras). Como dice Ilya, estaba buscando una solución más óptima, pero como ha señalado, probablemente ya haya obtenido la mejor solución. – Ant

+0

Si desea insertar de char al principio, deberá usar memmov() antes de reasignar el búfer ... Si realloc() no puede hacer crecer el bloque actual, copiará la memoria dos veces, una vez que el realloc(), una vez el memmov() la solución de Romulo copie siempre la memoria, pero solo una vez ...;) – alcuadrado

0

No - y si lo piensas bien, no puede funcionar. Entre que usted verifique lo que va a hacer y realmente lo haga, otro proceso podría asignar memoria. En una aplicación de subprocesos múltiples esto no puede funcionar. Mientras revisas lo que va a hacer y realmente lo haces, otro hilo podría asignar memoria.

Si le preocupa este tipo de cosas, podría ser el momento de mirar las estructuras de datos que está utilizando para ver si puede solucionar el problema allí. Dependiendo de cómo se construyan estas cadenas, puede hacerlo de manera bastante eficiente con un búfer bien diseñado.

+0

Otro proceso lo asignará en su espacio de direcciones por lo que no es relevante. – Ilya

+0

Simplemente reemplace "proceso" por "hilo". – Constantin

+0

¡Mejor punto! – Draemon

0

No creo que sea posible en una plataforma cruzada. Here es el código de la aplicación ulibc que pueden dar una idea de cómo hacerlo manera dependiente de la plataforma ITIN, en realidad, es mejor encontrar la fuente glibc pero éste era en la parte superior de búsqueda de google :)

comportamiento
+0

Considera usar http://tinyurl.com/ para URL demasiado largas. –

+0

Gracias he editado el enlace. – Ilya

+1

por favor no :-) http://www.codinghorror.com/blog/archives/001276.html –

0

Si obstacks son una buena opción para sus necesidades de asignación de memoria, puede utilizar su funcionalidad fast growing. Los obstacks son una característica de glibc, pero también están disponibles en la biblioteca libiberty, que es bastante portátil.

2

Como se señala en los comentarios, el caso 3 de la pregunta (sin memoria) es incorrecto; realloc() devolverá NULL si no hay memoria disponible [pregunta ahora corregida].

Steve McConnell en 'Code Complete' señala que si guarda el valor de retorno desde realloc() en la única copia del puntero original cuando realloc() falla, acaba de filtrar la memoria.Es decir:

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

Las diferentes implementaciones de realloc() se comportarán de manera diferente. Lo único seguro es suponer que los datos se moverán siempre, que siempre obtendrá una nueva dirección cuando realloc() la memoria.

Como alguien más señaló, si le preocupa esto, tal vez es hora de mirar sus algoritmos.

1

Guardar la cadena hacia atrás ayuda?

De lo contrario ... solo malloc() más espacio del que necesita, y cuando se quede sin espacio, cópielo en un nuevo búfer. Una técnica simple es duplicar el espacio cada vez; esto funciona bastante bien porque cuanto mayor sea la cadena (es decir, cuanto más tiempo lleve copiarse en un nuevo búfer) menos necesitará ocurrir.

Usando este método también puede justificar a la derecha su cadena en el búfer, por lo que es fácil agregar caracteres al inicio.

0

por qué no mantener un cierto espacio buffer vacío en la izquierda de la cadena, así:

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

Para añadir "sobre" al principio de la cadena para que sea "a \ 0":

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

De esta manera, no tendrá que seguir copiando su búfer cada vez que quiera hacer una inserción al principio.

Si tiene que hacer inserciones tanto al principio como al final, solo tiene espacio asignado en ambos extremos; las inserciones en el medio aún necesitarán barajar elementos, obviamente.

0

Un mejor enfoque es utilizar una lista vinculada. Haga que cada uno de sus objetos de datos se asigne en una página y asigne otra página y tenga un enlace a ella, ya sea desde la página anterior o desde una página de índice. De esta forma, sabrá cuándo fallará la próxima asignación y nunca tendrá que copiar la memoria.

Cuestiones relacionadas