Динамические массивы и матрицы
Динамический массив
Динамический массив — это блок памяти, размер которого задаётся во время выполнения.
Работают с ним как с обычным массивом: 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] | смешение размеров |
Документация
Что запомнить
- Динамический массив выделяют через
malloc(n * sizeof(*a))и освобождаютfree(a). - Массив структур выделяется так же, как массив чисел; элементы — структуры.
- Динамическая матрица
int **— массив указателей на строки. - Строки освобождают до массива указателей.
- Альтернатива — один блок
n * mс индексомi * m + j.