Недопустимый доступ
Выход за границы и неинициализированные данные
«Недопустимый доступ» — бесплатный урок C Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.
Не только утечки
Утечки расходуют память, но ошибки недопустимых обращений повреждают её. memcheck обнаруживает несколько видов таких ошибок:
- Чтение или запись за концом буфера
- Использование памяти после
free - Чтение неинициализированных значений
- Чтение или запись через недопустимый указатель
Из-за них возникают самые опасные и труднее всего воспроизводимые сбои.
Запись за границами
Эта программа выделяет место для 5 значений типа int, но записывает по индексу 5 — в шестой элемент.
Индекс 5 указывает на элемент сразу за концом массива. В этой строке memcheck сообщает об Invalid write of size 4.
#include <stdlib.h>
int main(void) {
int *a = malloc(5 * sizeof(int));
a[5] = 99; /* valid indices are 0..4 */
free(a);
return 0;
}Чтение сообщения об ошибке
Отчёт указывает операцию, размер и расположение относительно блока:
Invalid write of size 4Address 0x... is 0 bytes after a block of size 20 alloc'd
Фраза «0 bytes after a block of size 20» означает, что обращение произошло сразу после блока размером 20 байт (5 значений int).
Чтение за границами
Ошибки чтения тоже обнаруживаются. Здесь цикл выполняется на один элемент дольше.
На последней итерации memcheck сообщает об Invalid read of size 4, даже если программа случайно может «работать».
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *a = calloc(3, sizeof(int));
for (int i = 0; i <= 3; i++) /* should be i < 3 */
printf("%d\n", a[i]);
free(a);
return 0;
}Неинициализированные значения
malloc не обнуляет память. Использование выделенной памяти до присваивания значения приводит к непредсказуемому результату.
Когда такое значение влияет на управление потоком или вывод, memcheck сообщает Conditional jump or move depends on uninitialised value(s).
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *p = malloc(sizeof(int));
if (*p == 0) /* *p was never set */
printf("zero\n");
free(p);
return 0;
}Почему сообщения о неинициализированных данных появляются не сразу
memcheck не сообщает об ошибке в тот же момент, когда вы читаете неинициализированную память. Он отслеживает «определённость» данных и сообщает об ошибке только тогда, когда неопределённое значение действительно имеет значение — например, участвует в ветвлении, выводе или системном вызове.
Это позволяет избежать ложных предупреждений при безвредном копировании неинициализированных байтов.
Использование после освобождения
Обращение к памяти после её освобождения — это неопределённое поведение. memcheck обнаруживает его точно.
Он сообщает Invalid read of size 4 и добавляет примечание Address ... is 0 bytes inside a block of size 4 free'd, а также показывает, где был выполнен free.
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int *p = malloc(sizeof(int));
*p = 7;
free(p);
printf("%d\n", *p); /* read after free */
return 0;
}Двойное освобождение памяти
Повторное освобождение одного и того же указателя повреждает служебные данные распределителя памяти.
memcheck сообщает об Invalid free() и показывает как текущее освобождение, так и исходное выделение памяти, поэтому ошибка становится очевидной.
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
free(p);
free(p); /* freed twice */
return 0;
}Отслеживание источников
Для ошибок, связанных с неинициализированными значениями, добавьте --track-origins=yes.
После этого memcheck указывает, где было создано некорректное значение, а не только где оно использовалось, превращая расплывчатое предупреждение в точное указание на исправление.
valgrind --track-origins=yes ./progСтек и глобальные массивы
memcheck отлично обнаруживает ошибки в куче. Для переполнений стека или глобальных массивов его возможности слабее.
В таких случаях чаще всего лучше выбрать AddressSanitizer (gcc -fsanitize=address). Эти два инструмента дополняют друг друга.
Ошибка на единицу: нулевой терминатор
Частая причина переполнения кучи — забытый строковый терминатор. strlen возвращает 5 для 'hello', но для хранения завершающего '\0' строке требуется 6 байт.
memcheck сообщает об ошибке Invalid write, когда strcpy записывает этот последний байт за пределами строки.
#include <stdlib.h>
#include <string.h>
int main(void) {
char *s = malloc(strlen("hello")); /* needs +1 */
strcpy(s, "hello"); /* writes the '\0' past end */
free(s);
return 0;
}Быстрая проверка
Определите, о чём сообщает memcheck для этого фрагмента.
Итоги
Теперь Вы умеете находить ошибки недопустимого доступа:
- Чтение и запись за пределами границ обозначаются как Invalid read/write с указанием смещения относительно блока
- Неинициализированные значения отмечаются, когда влияют на решение или вывод
- Использование после освобождения и двойное освобождение обнаруживаются с указанием обоих мест вызова
--track-origins=yesуказывает источник неопределённых значений; для ошибок стека используйте его вместе с ASan
Далее: чтение полного отчёта.
Часто задаваемые вопросы
Урок «Недопустимый доступ» бесплатный?
Да — полный текст урока «Недопустимый доступ» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.
Чему я научусь в уроке «Недопустимый доступ»?
Выход за границы и неинициализированные данные Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C Academy?
Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Недопустимый доступ»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C Academy?
Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужен Valgrind
- Обнаружение утечек
- Недопустимый доступ
- Чтение отчётов