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



malloc parameter-passing (1)

Esta pregunta está destinada a ser utilizada como un duplicado canónico para 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 tiene lugar la asignación. Cuando intento usar los mismos datos fuera de la función, me cuelgo 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 print_array se llama a print_array desde dentro de la función create_array , obtengo el resultado esperado 0 1 2 3 4 , pero cuando lo llamo desde main , obtengo un bloqueo del programa.

¿Cuál es la razón para esto?


La razón de este error es que los data utilizados por la función create_array son una variable local que solo existe dentro de esa función. La dirección de memoria asignada obtenida de malloc solo se almacena en esta variable local y nunca se devuelve a la persona que llama.

Considere este simple 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 el parámetro x . Esto se conoce como paso por valor .

Cuando x se modifica, solo se modifica esa variable local. La variable a en la persona que llama permanece sin cambios, y dado que a no se inicializa, contendrá "basura" y no se puede usar de manera confiable.

Los punteros no son una excepción a esta regla de paso por valor. En su ejemplo, los data variables del puntero se pasan por valor a la función. El puntero de data dentro de la función es una copia local y la dirección asignada de malloc nunca se devuelve a la persona que llama.

Por lo tanto, la variable de puntero en la persona que llama permanece sin inicializar y, por lo tanto, el programa se bloquea. Además, la función create_array también ha creado una pérdida de memoria , ya que después de la ejecución de esa función, ya no hay ningún puntero en el programa que realice un seguimiento de ese fragmento de memoria asignada.

Hay dos formas de modificar la función para que funcione como se espera. 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 escribe directamente a 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);
}

Cualquiera de las formas está bien.





pass-by-value