2012-06-17 12 views
63

Estoy tratando de aprender C y actualmente estoy tratando de escribir una estructura básica de pila de datos, pero parece que no puedo obtener el malloc/free básico correcto.Memoria libre asignada en una función diferente?

Aquí está el código que he estado usando (Estoy publicar una pequeña parte para ilustrar un problema específico, no el código total, pero el mensaje de error se generó sólo con la ejecución de este código de ejemplo en valgrind)

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

typedef struct Entry { 
    struct Entry *previous; 
    int value; 
} Entry; 

void destroyEntry(Entry entry); 

int main(int argc, char *argv[]) 
{ 
    Entry* apple; 
    apple = malloc(sizeof(Entry)); 
    destroyEntry(*(apple)); 
    return 0; 
} 

void destroyEntry(Entry entry) 
{ 
    Entry *entry_ptr = &entry; 
    free(entry_ptr); 
    return; 
} 

Cuando lo ejecuto a través valgrind con --leak-check=full --track-origins=yes, me sale el siguiente error:

==20674== Invalid free()/delete/delete[]/realloc() 
==20674== at 0x4028E58: free (vg_replace_malloc.c:427) 
==20674== by 0x80485B2: destroyEntry (testing.c:53) 
==20674== by 0x8048477: main (testing.c:26) 
==20674== Address 0xbecc0070 is on thread 1's stack 

Creo que este error significa que la función destroyEntry no está autorizado a modificar la memoria al ubicado explícitamente en main. ¿Está bien? ¿Por qué no puedo simplemente free la memoria que asigné en main en otra función? (¿y este comportamiento es de alguna manera específico del principal?)

+17

+1 por pregunta clara y SSCCE. –

+7

@MatteoItalia Nunca antes había oído hablar de [SSCCE] (http://sscce.org/). Definitivamente es un buen concepto. Gracias por presentarme a eso. –

+0

Llamar por valor ??? –

Respuesta

50

Cada vez que pasa un parámetro a una función, se realiza una copia y la función funciona en esa copia. Entonces, en su caso, está intentando free una copia del objeto original, lo cual no tiene ningún sentido.

Debe modificar su función para tomar un puntero, y luego puede hacer que llame al free directamente en ese puntero.

36

Esto está pasando por valor, lo que significa que se crea una copia, por lo que intenta liberar la memoria, donde reside la variable local entry. Tenga en cuenta que entry es un objeto con duración de almacenamiento automática y la memoria donde reside se liberará automáticamente cuando su programa se salga del alcance de la función destroyEntry.

void destroyEntry(Entry entry) 
{ 
    Entry *entry_ptr = &entry; 
    free(entry_ptr); 
    return; 
} 

Su función debe tener un puntero (que pasa por referencia):

void destroyEntry(Entry *entry) 
{ 
    free(entry); 
} 

Entonces, en lugar de destroyEntry(*(apple)); que acaba de llamar destroyEntry(apple);. Tenga en cuenta que si no hay otra funcionalidad conectada con la función destroyEntry, es redundante y es mejor llamar directamente al free(apple).

9

Las otras respuestas aquí señalan el problema principal: porque usted desreferencia su manzana cuando llama a destroyEntry en main(), pasa por referencia, creando una copia.

Incluso una vez que conoces tu problema, es útil volver al error e intentar conectar el texto de lo que estás viendo con el problema, para que la próxima vez que veas sea más probable que pienses hacia fuera rápidamente. Encuentro que los errores C y C++ pueden parecer enloquecedoramente ambiguos a veces.

Generalmente, cuando tengo problemas para liberar punteros o eliminar objetos, me gusta imprimir las direcciones, especialmente cuando las asigno y justo cuando intento liberarlas. Valgrind ya te dio la dirección del puntero malo, pero ayuda a compararlo con uno bueno.

int main() 
{ 
    Entry * apple; 
    apple = malloc(sizeof(Entry)); 
    printf("apple's address = %p", apple); // Prints the address of 'apple' 
    free(apple); // You know this will work 
} 

Después de hacer eso, se daría cuenta de la sentencia printf() le dio una dirección algo así como 0x8024712 (sólo que componen una dirección en el rango general la derecha), pero su salida valgrind dio 0x4028E58. Notarás que están en dos lugares muy diferentes (de hecho, "0x4 ..."está en la pila, no es el montón de donde malloc() asigna, pero supongo que si recién estás empezando, eso no es una señal de alerta para ti), entonces sabes que estás tratando de liberar memoria de la lugar incorrecto, por lo tanto "inválido libre()".

Así que desde allí puede decirse "De acuerdo, de alguna manera mi puntero se está corrompiendo". Ya redujo su problema a un pequeño ejemplo compilable, por lo que ganó tardarás en resolverlo desde allí.

TL; DR: cuando te encuentres con errores relacionados con el puntero, prueba a imprimir las direcciones o encuéntralas en tu depurador favorito. Por lo general, al menos te apunta en la dirección correcta

Nada de esto es para desalentar la publicación de su pregunta en Stack Exchange, por supuesto. Cientos de programadores probablemente se beneficiarán de que lo hayas hecho.

+0

Gracias por publicar esto ... es un buen punto para descubrir errores con punteros y cosas así. He estado aprendiendo sobre stack vs. heap, pero no me di cuenta de que tenían direcciones de memoria generalizadas. (Entonces, 'NULL' ->' 0x0', 'stack' ->' 0x4' y heap es cualquier otra cosa?). –

+0

Además, no puedo hacer un cambio de carácter en tu publicación, pero en la cadena printf: '" apple's address =% d "' la cadena de formato debería ser '% p', no'% d', ¿verdad? –

+0

No puedo decir con 100% de certeza que las direcciones de montón siempre serán '0x8 ...' y las direcciones de pila siempre serán '0x4 ...'. Para ser honesto, hago la mayoría de los programas incrustados en estos días donde el espacio de direcciones está muy bien definido y tu proceso es el único en ejecución. Sin embargo, tengo entendido que en x86, cada proceso obtiene su propio espacio de direcciones virtuales y, en mi experiencia, esas "reglas" parecen ser ciertas. Gracias por señalar el problema% d. Funciona (ya que imprime la dirección), pero es más difícil de leer. Usualmente uso "0x% x", ya que así es como estoy acostumbrado a verlo. – gkimsey

Cuestiones relacionadas