El acceso a la memoria dinámica solo funciona dentro de la función
Esta pregunta debe utilizarse como duplicado canónico de estas preguntas frecuentes:
Estoy asignando datos dinámicamente dentro de una función y todo funciona bien, pero solo dentro de la función donde se realiza la asignación. Cuando intento utilizar los mismos datos fuera de la función, obtengo fallas u otro comportamiento inesperado del programa.
Aquí hay un MCVE:
#include <stdlib.h>
#include <stdio.h>
void create_array (int* data, int size)
{
data = malloc(sizeof(*data) * size);
for(int i=0; i<size; i++)
{
data[i] = i;
}
print_array(data, size);
}
void print_array (int* data, int size)
{
for(int i=0; i<size; i++)
{
printf("%d ", data[i]);
}
printf("\n");
}
int main (void)
{
int* data;
const int size = 5;
create_array(data, size);
print_array(data, size); // crash here
free(data);
}
Cada vez que print_array
se llama desde dentro de la create_array
función, obtengo el resultado esperado 0 1 2 3 4
, pero cuando lo llamo desde main
, se produce un bloqueo del programa.
¿Cuál es la razón para esto?
El motivo de este error es que lo data
utilizado por la create_array
función es una variable local que solo existe dentro de esa función. La dirección de memoria asignada obtenida malloc
solo se almacena en esta variable local y nunca se devuelve a la persona que llama.
Considere este sencillo ejemplo:
void func (int x)
{
x = 1;
printf("%d", x);
}
...
int a;
func(a);
printf("%d", a); // bad, undefined behavior - the program might crash or print garbage
Aquí, una copia de la variable a
se almacena localmente dentro de la función, como parámetro x
. Esto se conoce como paso por valor .
Cuando x
se modifica, solo se cambia esa variable local. La variable a
en la persona que llama permanece sin cambios y, como a
no está inicializada, contendrá "basura" y no podrá usarse de manera confiable.
Los punteros no son una excepción a esta regla de paso por valor. En su ejemplo, la variable de puntero data
se pasa por valor a la función. El data
puntero dentro de la función es una copia local y la dirección asignada malloc
nunca se devuelve a la persona que llama.
Entonces, la variable de puntero en la persona que llama permanece sin inicializar y, por lo tanto, el programa falla. Además, la create_array
función también ha creado una pérdida de memoria , ya que después de la ejecución de la función, ya no hay ningún puntero en el programa que realice un seguimiento de esa porción de memoria asignada.
Hay dos formas de modificar la función para que funcione como se esperaba. Ya sea devolviendo una copia de la variable local a la persona que llama:
int* create_array (int size)
{
int* data = malloc(sizeof(*data) * size);
for(int i=0; i<size; i++)
{
data[i] = i;
}
print_array(data, size);
return data;
}
int main (void)
{
int* data;
const int size = 5;
data = create_array(size);
print_array(data, size);
}
o pasando la dirección a la variable de puntero de la persona que llama y escribiendo directamente en la variable de la persona que llama:
void create_array (int** data, int size)
{
int* tmp = malloc(sizeof(*tmp) * size);
for(int i=0; i<size; i++)
{
tmp[i] = i;
}
*data = tmp;
print_array(*data, size);
}
int main (void)
{
int* data;
const int size = 5;
create_array(&data, size);
print_array(data, size);
}
Cualquier forma está bien.