C Academy · Урок

Висячие указатели

Опасности использования освобождённой памяти

Урок 4 из 413 шагов

«Висячие указатели» — бесплатный урок C Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Что такое висячий указатель

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

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

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

int main(void) {
    int *p = malloc(sizeof(int));
    *p = 5;
    printf("%d\n", *p);
    free(p);
    p = NULL;
    return 0;
}

Использование после освобождения

Наиболее распространённая причина — чтение или запись через указатель после вызова free.

Этот блок памяти мог быть повторно использован для чего-то другого.

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

int main(void) {
    int *p = malloc(sizeof(int));
    *p = 42;
    free(p);
    /* Reading *p here would be use-after-free. */
    p = NULL;
    printf("avoided use-after-free\n");
    return 0;
}

Обнуление после освобождения

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

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

int main(void) {
    char *buf = malloc(10);
    buf[0] = 'A';
    free(buf);
    buf = NULL;
    if (buf == NULL) printf("buf is safely null\n");
    return 0;
}

Возврат локального адреса

Возврат адреса локальной переменной (из стека) создаёт висячий указатель: переменная исчезает после возврата функции.

Вместо этого используйте кучу или передавайте буфер в функцию.

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

int *make_value(int v) {
    int *p = malloc(sizeof(int));
    *p = v;
    return p;
}

int main(void) {
    int *p = make_value(99);
    printf("%d\n", *p);
    free(p);
    return 0;
}

Несколько указателей на один блок

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

Отслеживайте владение памятью, чтобы освобождать её мог только владелец.

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

int main(void) {
    int *a = malloc(sizeof(int));
    *a = 7;
    int *b = a;
    printf("%d\n", *b);
    free(a);
    a = NULL;
    b = NULL;
    printf("both nulled\n");
    return 0;
}

Висячий указатель после realloc

Если realloc перемещает блок, любой старый указатель на прежнее расположение становится висячим.

Используйте только указатель, который вернул realloc.

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

int main(void) {
    int *a = malloc(sizeof(int));
    a[0] = 1;
    int *b = realloc(a, 50 * sizeof(int));
    /* a may now be dangling; use only b. */
    b[0] = 2;
    printf("%d\n", b[0]);
    free(b);
    return 0;
}

Освобождение памяти во вспомогательной функции

Если вспомогательная функция освобождает блок, указатель вызывающей функции становится висячим. Обнулите его после вызова или спроектируйте вспомогательную функцию так, чтобы она принимала указатель на указатель.

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

void destroy(int **p) {
    free(*p);
    *p = NULL;
}

int main(void) {
    int *p = malloc(sizeof(int));
    *p = 5;
    destroy(&p);
    printf("p is %s\n", p == NULL ? "null" : "set");
    return 0;
}

Почему это опасно

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

Висячие указатели при завершении области видимости

Указатель на переменную, действующую только внутри блока, становится недействительным после завершения блока. Не храните указатели дольше, чем существует объект, на который они указывают.

#include <stdio.h>

int main(void) {
    int *p;
    {
        int x = 10;
        p = &x;
        printf("in scope: %d\n", *p);
    }
    /* p now dangles; do not dereference it here. */
    printf("block ended\n");
    return 0;
}

Обнаружение висячих указателей

Инструменты AddressSanitizer и Valgrind обнаруживают использование памяти после освобождения во время выполнения и указывают точную строку.

Во время разработки собирайте программу с включёнными средствами проверки.

Безопасный жизненный цикл в коде

Выделите память, используйте её, освободите и обнулите указатель. Последовательное соблюдение этого порядка не позволяет указателям становиться висячими.

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

int main(void) {
    int *p = malloc(sizeof(int));
    if (!p) return 1;
    *p = 123;
    printf("%d\n", *p);
    free(p);
    p = NULL;
    return 0;
}

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете висячие указатели.

Итоги

Вы узнали о висячих указателях:

  • Они указывают на освобождённую память или память за пределами области видимости; их использование является неопределённым поведением.
  • Не возвращайте адреса локальных переменных; никогда не используйте указатель после free или после перемещения блока функцией realloc.
  • После освобождения присваивайте указателям значение NULL и отслеживайте владение памятью.
  • Используйте средства проверки и Valgrind для обнаружения использования памяти после освобождения.
Можно начать бесплатно

Изучай C с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
144

Часто задаваемые вопросы

Урок «Висячие указатели» бесплатный?

Да — полный текст урока «Висячие указатели» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Висячие указатели»?

Опасности использования освобождённой памяти Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Висячие указатели»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. malloc и free
  2. calloc и realloc
  3. Утечки памяти
  4. Висячие указатели
← Назад к C Academy