Динамический доступ к памяти работает только внутри функции

Этот вопрос предназначен для использования в качестве канонического дубликата для этого FAQ:

Я динамически распределяю данные внутри функции, и все работает хорошо, но только внутри функции, в которой происходит распределение. Когда я пытаюсь использовать те же данные вне функции, я получаю сбои или другое неожиданное поведение программы.

Вот MCVE:

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

void print_array (int* data, int size)
{
  for(int i=0; i<size; i++)
  {
    printf("%d ", data[i]);
  }
  printf("\n");
}


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);
}


int main (void)
{
  int* data;
  const int size = 5;

  create_array(data, size);
  print_array(data, size);  // crash here

  free(data);
}

Всякий раз, когда print_array вызывается изнутри create_array функция, я получаю ожидаемый результат 0 1 2 3 4, но когда я звоню из mainЯ получаю сбой программы.

Что является причиной этого?

1 ответ

Решение

Причина этой ошибки в том, что data используется create_array Функция - это локальная переменная, которая существует только внутри этой функции. Назначенный адрес памяти, полученный из malloc хранится только в этой локальной переменной и никогда не возвращается вызывающей стороне.


Рассмотрим этот простой пример:

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

Здесь копия переменной a хранится локально внутри функции, как параметр x, Это известно как передача по значению.

когда x модифицируется, изменяется только локальная переменная. Переменная a в звонилке остается неизменным, а с a не инициализируется, он будет содержать "мусор" и не может быть надежно использован.


Указатели не являются исключением из этого правила передачи по значению. В вашем примере переменная указателя data передается по значению в функцию. data указатель внутри функции является локальной копией и назначенный адрес из malloc никогда не передается обратно вызывающей стороне.

Таким образом, переменная-указатель в вызывающей стороне остается неинициализированной, и, следовательно, происходит сбой программы. В дополнение create_array Функция также создала утечку памяти, так как после выполнения этой функции в программе больше нет указателя, отслеживающего этот кусок выделенной памяти.


Есть два способа изменить функцию, чтобы она работала как положено. Либо возвращая копию локальной переменной обратно вызывающей стороне:

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);
}

или путем передачи адреса в переменную-указатель вызывающей стороны и записи непосредственно в переменную вызывающей стороны:

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);
}

Любая форма в порядке.

Другие вопросы по тегам