Malloc и free в C: память без утечек в первом примере

malloc и free нужны, когда размер данных становится известен только во время работы программы. Обычный массив int scores[5] подходит, если размер заранее понятен. Но если пользователь вводит количество элементов, программа должна выделить память динамически

В этом уроке мы сделаем первый безопасный пример: спросим размер массива, выделим память через malloc, проверим NULL, заполним массив, посчитаем сумму и обязательно освободим память через free

Что получится в конце

Файл dynamic_array.c:

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

int main(void)
{
    int count = 0;

    printf("How many numbers: ");
    if (scanf("%d", &count) != 1 || count <= 0) {
        printf("Positive number expected\n");
        return 1;
    }

    int *numbers = malloc((size_t)count * sizeof(numbers[0]));
    if (numbers == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    for (int i = 0; i < count; i++) {
        numbers[i] = i + 1;
    }

    int sum = 0;
    for (int i = 0; i < count; i++) {
        sum += numbers[i];
    }

    printf("Sum: %d\n", sum);

    free(numbers);
    numbers = NULL;

    return 0;
}

Сборка:

gcc -Wall -Wextra -std=c17 dynamic_array.c -o dynamic_array
./dynamic_array

Зачем нужен malloc

Обычный массив:

int numbers[5];

имеет размер, известный в момент написания кода. Иногда этого достаточно. Но если размер вводит пользователь, приходит из файла или зависит от результата вычислений, нужен другой способ

malloc просит у системы блок памяти:

int *numbers = malloc((size_t)count * sizeof(numbers[0]));

Функция возвращает указатель на начало выделенного блока. Если память выделить не удалось, возвращается NULL

Важно: malloc не знает, что вы хотите массив int. Она получает только количество байтов. Поэтому мы считаем размер так:

(size_t)count * sizeof(numbers[0])

Так код остается устойчивым, даже если тип массива позже изменится

Почему нужна проверка NULL

После malloc всегда проверяйте результат:

if (numbers == NULL) {
    printf("Memory allocation failed\n");
    return 1;
}

В маленькой учебной программе память почти всегда выделится, но привычка нужна сразу. В C нельзя безопасно писать в память, если указатель равен NULL. Попытка обратиться к numbers[i] после неудачного malloc приведет к ошибке выполнения

Порядок такой:

  1. Посчитать размер
  2. Вызвать malloc
  3. Проверить NULL
  4. Использовать память
  5. Вызвать free

numbers[i] работает и с динамической памятью

После успешного malloc можно использовать индексный синтаксис:

numbers[i] = i + 1;

Это удобно: указатель на первый элемент ведет себя похоже на массив в выражениях с индексами. Но ответственность за границы остается на вас. Если выделили count элементов, допустимые индексы идут от 0 до count - 1

Цикл:

for (int i = 0; i < count; i++) {
    numbers[i] = i + 1;
}

останавливается до count, поэтому не выходит за пределы блока

free: возвращаем память

Память, полученную через malloc, нужно освобождать:

free(numbers);
numbers = NULL;

free сообщает системе, что блок больше не нужен. Если забывать free, программа будет терять память. Для короткого учебного запуска это может быть незаметно, но для долгоживущей программы утечки становятся реальной проблемой

После free указатель лучше обнулить. Это не освобождает память повторно, но снижает риск случайно использовать старый адрес

Нельзя вызывать free для памяти, которую вы не получили через malloc, calloc или realloc. Нельзя освобождать один и тот же блок дважды

malloc, calloc и realloc коротко

Для первого урока достаточно malloc, но рядом часто встречаются еще две функции

calloc выделяет память и заполняет ее нулями:

int *items = calloc((size_t)count, sizeof(items[0]));

realloc меняет размер уже выделенного блока:

int *new_items = realloc(items, new_count * sizeof(items[0]));

С realloc нужно быть осторожным: если писать результат сразу в старый указатель, можно потерять адрес исходного блока при ошибке. Поэтому realloc лучше изучать отдельно, когда базовый цикл malloc -> проверка -> free уже понятен

Как проверить утечку в учебном коде

Самая простая проверка — пройти глазами все выходы из функции. Если после успешного malloc есть ветка с return, до нее должен быть free

Например:

if (some_error) {
    free(numbers);
    return 1;
}

В реальных проектах используют санитайзеры и анализаторы. Для первых шагов полезна сборка с AddressSanitizer, если ваш компилятор поддерживает:

gcc -Wall -Wextra -std=c17 -fsanitize=address dynamic_array.c -o dynamic_array

Это не замена пониманию, но хороший способ увидеть ошибки памяти раньше

Мини-практика: функция, которая создает массив

В реальном коде выделение памяти часто прячут в функцию. Тогда важно явно договориться, кто освобождает память

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

int *create_numbers(size_t count)
{
    int *numbers = malloc(count * sizeof(numbers[0]));
    if (numbers == NULL) {
        return NULL;
    }

    for (size_t i = 0; i < count; i++) {
        numbers[i] = (int)(i + 1);
    }

    return numbers;
}

int main(void)
{
    size_t count = 5;
    int *numbers = create_numbers(count);

    if (numbers == NULL) {
        printf("Allocation failed\n");
        return 1;
    }

    for (size_t i = 0; i < count; i++) {
        printf("%d\n", numbers[i]);
    }

    free(numbers);
    return 0;
}

Функция create_numbers выделяет память и возвращает указатель. Но освобождает эту память уже вызывающий код в main. Это нормальная схема, если она явно понятна. В больших проектах такие правила называют владением ресурсом: кто получил владение, тот должен освободить

Одно место освобождения ресурса

Когда в функции много проверок, легко забыть free на одной из веток. В C часто используют стиль с одним блоком очистки в конце функции. Для маленьких уроков это может выглядеть избыточно, но идея полезная

int status = 0;
int *numbers = malloc(count * sizeof(numbers[0]));

if (numbers == NULL) {
    status = 1;
    goto cleanup;
}

/* work with numbers */

cleanup:
free(numbers);
return status;

goto не нужен в каждом учебном примере, но в C его иногда используют именно для очистки ресурсов, а не для хаотичных переходов. Главное — не оставить память без освобождения, если после malloc что-то пошло не так

Что делает free с NULL

В стандартной библиотеке вызов free(NULL) допустим и ничего не делает. Это удобно для очистки, потому что можно писать:

free(numbers);

даже если numbers равен NULL. Но это не отменяет проверку после malloc: использовать numbers[i] при numbers == NULL все равно нельзя

После успешного free указатель лучше больше не трогать. Обнуление numbers = NULL; помогает избежать повторного использования старого адреса, но не делает старые копии этого указателя безопасными

Частые ошибки и порядок проверки

Не проверили результат malloc Всегда проверяйте numbers == NULL перед первым обращением к массиву

Посчитали размер вручную через sizeof(int) sizeof(numbers[0]) обычно удобнее и безопаснее. Если тип поменяется, формула останется правильной

Вызвали free слишком рано После free(numbers) нельзя читать numbers[i] или писать туда. Блок больше не ваш

Забыли free на ветке ошибки Если ошибка происходит после выделения памяти, перед return нужно освободить уже выделенный блок

Что может быть еще интересно по этой теме

Где находится память malloc: stack или heap? Обычно автоматические переменные живут в стеке, а память malloc — в heap. Для новичка важнее практическое правило: что выделили через malloc, то освобождайте через free

*Нужно ли приводить результат malloc к int?** В C обычно не нужно. Достаточно подключить <stdlib.h>, чтобы компилятор видел объявление malloc

Почему после free указатель не становится NULL сам? free получает копию значения указателя. Она освобождает блок памяти, но не может изменить вашу переменную-указатель без передачи адреса этой переменной

Что изучать после динамической памяти? Структуры. Вместе struct, указатели и malloc позволяют хранить более реальные данные, чем просто массив чисел

Что почитать дальше по C

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x