Как взаимодействует выделение памяти для динамического массива и вызов функций в 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 шт):

Автор решения: Qwertiy
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;
}
→ Ссылка
Автор решения: dark_buckwheat

Решение предложенное @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() в конечном варианте функции.

→ Ссылка