C free (): puntero inválido asignado en otra función

Soy nuevo en StackOverflow. Estoy aprendiendo puntero C ahora.

Este es mi código:

#include  #include  int alloc(int* p){ p = (int*) malloc (sizeof(int)); if(!p){ puts("fail\n"); return 0; } *p = 4; printf("%d\n",*p); return 1; } int main(){ int* pointer; if(!alloc(pointer)){ return -1; }else{ printf("%d\n",*pointer); } free(pointer); return 0; } 

Yo compilo con: gcc -o main main.c

error: free (): puntero inválido: 0xb77ac000 ***

¿Qué hay de malo con mi código?

Los argumentos en C siempre se pasan por valor . Por lo tanto, cuando llama a alloc(pointer) , simplemente pasa el valor que contiene el pointer basura. Dentro de la función, la asignación p = (int*)... solo modifica la variable / argumento local p . En su lugar, necesita pasar la dirección del pointer a alloc , así:

 int alloc(int **p) { *p = malloc(sizeof(int)); // side note - notice the lack of a cast ... **p = 4; // <---- notice the double indirection here printf("%d\n", **p); // <---- same here return 1; } 

En main, llamarías a alloc así:

 if (!(alloc(&pointer))) { .... 

Entonces, su código funcionará.

Todo en C es pasado por valor. Esto significa que las funciones siempre operan en su propia copia local de lo que se pasa a la función. Por lo general, los punteros son una buena manera de imitar un esquema de paso por referencia porque un puntero y una copia de ese puntero contienen la misma dirección de memoria. En otras palabras, un puntero y su copia apuntan al mismo espacio.

En su código, el problema es que la función alloc obtiene su propia copia local del puntero que está pasando. Así que cuando hace p = (int*) malloc (sizeof(int)); está cambiando el valor de p para que sea una nueva dirección de memoria, pero el valor de pointer en main permanece sin cambios.

Puede evitar esto pasando un puntero a un puntero, o devolviendo el nuevo valor de p .

Tienes dos problemas principales en tu código.

Primero, la función de alloc crea un puntero a través de malloc , pero nunca lo libera, ni tampoco devuelve el puntero a la función de llamada. Esto garantiza que la memoria de las direcciones del puntero nunca se pueda liberar a través del comando free , y ahora tiene pérdidas de memoria.

En segundo lugar, la variable, int* pointer en main , no se está modificando como pensaría. En C, los argumentos de la función se “pasan por valor”. Tienes dos formas de abordar este problema:

  1. Pase un puntero a la variable que desea modificar (en su caso, un puntero a un puntero a un int)
  2. Haga que la función devuelva el puntero a la función que lo llamó.

Aquí hay dos implementaciones de mis recomendaciones:

Enfoque 1


 #include  #include  int alloc(int** p); int alloc(int** p) { if (!p) { printf("Invalid argument\n"); return (-1); } if ((*p = (int*)malloc(sizeof(int))) == NULL) { printf("Memory allocation error\n"); return (-1); } **p = 123; printf("p:%p - *p:%p - **p:%d\n", p, *p, **p); return 0; } int main(){ int* pointer; if(alloc(&pointer) != 0){ printf("Error calling function\n"); }else{ printf("&pointer:%p- pointer:%p- *pointer:%d\n", &pointer, pointer, *pointer); } free(pointer); return 0; } 

Ejemplo de ejecución para el enfoque 1


 p:0xbfbea07c - *p:0x8656008 - **p:123 &pointer:0xbfbea07cointer - pointer:0x8656008ointer - *pointer:123 

Enfoque 2


 #include  #include  int* alloc(void) { int* p; if ((p = (int*)malloc(sizeof(int))) == NULL) { printf("Memory allocation error\n"); return (NULL); } *p = 123; printf("p:%p - *p:%d\n", p, *p); return p; } int main(){ int* pointer = alloc(); if(pointer == NULL) { printf("Error calling function\n"); }else{ printf("&pointer:%p- pointer:%p- *pointer:%d\n", &pointer, pointer, *pointer); } free(pointer); pointer = NULL; return 0; } 

Ejecución de la muestra para el Enfoque 2


 p:0x858e008 - *p:123 &pointer:0xbf9bb1ac- pointer:0x858e008- *pointer:123 

Usted está pasando el puntero por valor a su función de alloc . Aunque esa función lleva un puntero a un int, ese puntero no puede ser modificado por la función. Si hace alloc acepta **p , establezca *p = ... , y pase &pointer desde main, debería funcionar.

 #include  #include  int alloc(int** p){ *p = (int*) malloc (sizeof(int)); if(!*p){ puts("fail\n"); return 0; } **p = 4; printf("%d\n",**p); return 1; } int main() { int* pointer; if(!alloc(&pointer)){ return -1; } else { printf("%d\n",*pointer); } free(pointer); return 0; } 

Si desea que una función escriba en un parámetro no de matriz de tipo T , debe pasar un puntero a ese parámetro.

 void func( T *ptr ) { *ptr = new_value; } void foo ( void ) { T var; func( &var ); // writes new value to var } 

Si T es un tipo de puntero Q * , se vería como

 void func( Q **ptr ) { *ptr = new_pointer_value; } void foo ( void ) { Q *var; func( &var ); // writes new pointer value to var } 

Si Q es un tipo de puntero R * , obtendrías

 void func( R ***ptr ) { *ptr = new_pointer_to_pointer_value; } void foo ( void ) { R **var; func( &var ); // writes new pointer to pointer value to var } 

El patrón es el mismo en los tres casos; está pasando la dirección de la variable var , por lo que el parámetro formal ptr debe tener un nivel más de ptr indirecto que el parámetro real var .

Una lila silística: en lugar de escribir.

 p = (int *) malloc( sizeof (int) ); 

utilizar

 p = malloc( sizeof *p ); 

en lugar.

En C (a partir del estándar de 1989), no es necesario emitir el resultado de malloc ; void punteros void pueden asignarse a otros tipos de punteros y viceversa sin necesidad de un lanzamiento (esto no es cierto en C ++, pero si está escribiendo C ++, debería usar el new operador en lugar de malloc ). Además, bajo la versión de 1989 del idioma, usar el reparto enmascararía un error si olvidó incluir stdlib.h o, de lo contrario, no tenía una statement de scope de malloc . Sin embargo, eso no ha sido un problema desde la versión de 1999, así que ahora es más una cuestión de legibilidad que cualquier otra cosa.

El tipo de expresión *p es int , por lo que el resultado de sizeof *p es el mismo que el resultado de sizeof (int) . De esta manera, si alguna vez cambia el tipo de p , no tiene que modificar la llamada malloc .

Para asignar una matriz de valores, usarías algo como

 T *p = malloc( sizeof *p * NUM_ELEMENTS ); 

o, si desea que todo se ponga a cero inicialmente, use

 T *p = calloc( sizeof *p, NUM_ELEMENTS );