Как взаимодействует выделение памяти для динамического массива и вызов функций в C?
Я пишу небольшую игру для консоли на C. Игра представляет собой трёхмерного сапёра с возможностью настраивать длину поля вдоль осей. Игровое поле я храню в переменной field, которая является динамическим массивом, элементами которого являются строки (то есть массивы char длинны 3). Таким образом, поле является двумерным массивом char (если моё понимание логики указатель\массив верно).
В процессе написания программы, я решил разбить код на множество файлов по принципу "одна функция - один файл". Я вынес кусок кода, отвечавший за выделение памяти, без изменений в функцию int mem(char** field, size_t field_size).
Сам код:
int mem(char** field, size_t field_size)
{
size_t i;
field = malloc(field_size * sizeof(field[0]));
if (field == NULL) {
free(field);
return 1;
}
for (i = 0; i < field_size; i++)
field[i] = NULL;
for (i = 0; i < field_size; i++) {
field[i] = malloc(3 * sizeof(field[0][0]));
}
for (i = 0; i < field_size; i++) {
if (field[i] == NULL) {
free_field(field, field_size);
return 2;
}
}
return 0;
}
Функция free_field(field, field_size) - рукописная функция для освобождения памяти, выделенной на поле.
Проблема в том, что после вынесения этой логики в отдельный файл, в переменной field после вызова функции остаётся дефолтный NULL. Причём, как показала проверка, внутри самой функции память выделяется корректно(сама функция точно вызывается внутри main(), так что проблема не в том, что я забыл написать её вызов). Поэтому, я подозреваю, что проблема в том, что field начинает рассматриваться как внутренняя переменная самой функции, а все изменения изнутри не сказываются на изначальном field в основной программе.
Правильно ли я понял ситуацию, и как её лучше исправлять? Стоит ли мне переписать функцию, чтобы внутрь неё подавалась не сам field, а ссылка на него?
Ответы (2 шт):
int mem(char** field, size_t field_size)
int mem(char*** field, size_t field_size)
field = malloc(field_size * sizeof(field[0]));
*field = malloc(field_size * sizeof(field[0]));
И так далее.
Но учитывая, что ты не очищаешь старый field, можно лучше (не проверял, но идея должна быть понятна):
char **mem(size_t field_size)
{
size_t i;
char **field = malloc(field_size * sizeof(char*));
if (!field)
return 0;
memset(field, 0, field_size * sizeof(char*));
for (i = 0; i < field_size; i++)
{
field[i] = malloc(3 * sizeof(field[0][0]));
if (!field[i])
{
free_field(field, field_size);
return 0;
}
}
return field;
}
Решение предложенное @0andriy в комментарии к моему вопросу оказалось наиболее удобным и простым решением. Приведу ниже полный код итоговой функции:
int mem(char*** field_pointer, size_t field_size)
{
size_t i;
char** field = NULL;
field = malloc(field_size * sizeof(field[0]));
if (field == NULL)
{
free(field);
return 1;
}
for (i = 0; i < field_size; i++) field[i] = NULL;
for (i = 0; i < field_size; i++)
{
field[i] = malloc(3 * sizeof(field[0][0]));
}
for (i = 0; i < field_size; i++)
{
if (field[i] == NULL)
{
free_field(field, field_size);
return 2;
}
}
*field_pointer = field;
return 0;
}
PS: Замечания, высказанные по поводу структуры действительно справедливы, однако проект пишется с учебно-практической целью, поэтому я не использую calloc() в конечном варианте функции.