El acceso a la memoria dinámica solo funciona dentro de la función

Resuelto Lundin asked hace 8 años • 1 respuestas

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_arrayse llama desde dentro de la create_arrayfunció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?

Lundin avatar Sep 14 '16 16:09 Lundin
Aceptado

El motivo de este error es que lo datautilizado por la create_arrayfunción es una variable local que solo existe dentro de esa función. La dirección de memoria asignada obtenida mallocsolo 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 ase almacena localmente dentro de la función, como parámetro x. Esto se conoce como paso por valor .

Cuando xse modifica, solo se cambia esa variable local. La variable aen la persona que llama permanece sin cambios y, como ano 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 datase pasa por valor a la función. El datapuntero dentro de la función es una copia local y la dirección asignada mallocnunca 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_arrayfunció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.

Lundin avatar Sep 14 '2016 09:09 Lundin