Динамические массивы и матрицы

Динамический массив

Динамический массив — это блок памяти, размер которого задаётся во время выполнения. Работают с ним как с обычным массивом: a[i], &a[i], передача в функцию. Отличие одно: память нужно освободить самому.

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

int main(void)
{
    int n;
    if (scanf("%d", &n) != 1 || n <= 0)
    {
        return 1;
    }

    int *a = malloc((size_t)n * sizeof(*a));
    if (a == NULL)
    {
        return 1;
    }

    for (int i = 0; i < n; i++)
    {
        if (scanf("%d", &a[i]) != 1)
        {
            free(a);
            return 1;
        }
    }

    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        sum += a[i];
    }
    printf("Сумма: %d\n", sum);

    free(a);
    a = NULL;
    return 0;
}

После malloc элементы не инициализированы. Если нужен массив из нулей, используйте calloc((size_t)n, sizeof(*a)).

Массив структур

Структура — обычный тип, поэтому массив структур выделяется так же:

typedef struct
{
    int id;
    int course;
    double score;
} Student;

int n;
if (scanf("%d", &n) != 1 || n < 0)
{
    return 1;
}

Student *group = malloc((size_t)n * sizeof(*group));
if (group == NULL)
{
    return 1;
}

for (int i = 0; i < n; i++)
{
    if (scanf("%d %d %lf", &group[i].id, &group[i].course, &group[i].score) != 3)
    {
        free(group);
        return 1;
    }
}

/* ... обработка ... */

free(group);
group = NULL;

group[i] — структура, поэтому к полям обращаются через точку. &group[i] — адрес i-й структуры; его можно передать функции, которая должна изменить запись.

Динамическая матрица: массив указателей

Матрицу n × m удобно представить как массив из n указателей на строки:

int **a = malloc((size_t)n * sizeof(*a));
if (a == NULL)
{
    return 1;
}

for (int i = 0; i < n; i++)
{
    a[i] = malloc((size_t)m * sizeof(*a[i]));
    if (a[i] == NULL)
    {
        for (int k = 0; k < i; k++)
        {
            free(a[k]);
        }
        free(a);
        return 1;
    }
}

Обращение a[i][j] означает: взять i-ю строку, затем j-й элемент.

Заполнение и вывод:

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        if (scanf("%d", &a[i][j]) != 1)
        {
            /* освободить все строки и a */
            return 1;
        }
    }
}

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        printf("%4d", a[i][j]);
    }
    printf("\n");
}

Освобождение — в обратном порядке: сначала строки, потом массив указателей.

for (int i = 0; i < n; i++)
{
    free(a[i]);
}
free(a);
a = NULL;

Если сначала освободить a, до строк уже не добраться — получится утечка.

Матрица одним блоком

Вместо n отдельных строк можно выделить один непрерывный блок:

int *a = malloc((size_t)n * m * sizeof(*a));
if (a == NULL)
{
    return 1;
}

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        a[i * m + j] = 0;       /* элемент (i, j) */
    }
}

free(a);

Память лежит одним куском, освобождается одним free, но запись a[i][j] недоступна: индекс вычисляется вручную.

Что выбрать

Массив указателей (int **)Один блок (int *)
Обращениеa[i][j]a[i * m + j]
Выделениеn + 1 вызововодин malloc
Освобождениецикл + free(a)один free
Гибкие размеры строкданет
Наглядностьвышениже

Частые ошибки

ОшибкаЧто не так
malloc(sizeof(int) * n) для int **выделен размер чисел, а не указателей
забыли выделить строку a[i]обращение по «мусорному» адресу
освободили a, потом a[i]к строкам больше нет доступа, утечка
free(a) без освобождения строкутечка всех строк
перепутали n и m при выделениивыход за границы
не проверили каждый mallocпадение при нехватке памяти
a[i * m + j] записали как a[i * n + j]смешение размеров

Документация

Что запомнить

  1. Динамический массив выделяют через malloc(n * sizeof(*a)) и освобождают free(a).
  2. Массив структур выделяется так же, как массив чисел; элементы — структуры.
  3. Динамическая матрица int ** — массив указателей на строки.
  4. Строки освобождают до массива указателей.
  5. Альтернатива — один блок n * m с индексом i * m + j.

Подтверждение

—