Динамический доступ к памяти работает только внутри функции
Этот вопрос предназначен для использования в качестве канонического дубликата для этого 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);
}
Любая форма в порядке.