Нулевой указатель
Иногда указателю не на что указывать. Представьте функцию, которая ищет в массиве температур первый мороз, то есть температуру ниже нуля, и возвращает её адрес. Что ей вернуть, если за всю неделю мороза не было? Для этого в C есть значение NULL, нулевой указатель: указатель, который не указывает никуда.
Эта функция возвращает адрес, поэтому её тип возвращаемого значения — int *. &numbers[i] — адрес элемента с индексом i:
int *find_negative(int *numbers, int count) {
for (int i = 0; i < count; i++) {
if (numbers[i] < 0) {
return &numbers[i];
}
}
return NULL;
}Почему она возвращает адрес, а не саму температуру? Потому что число не может сказать «такой нет»: любое число, которое вернула бы функция, могло бы оказаться настоящей температурой. Указатель может, ведь NULL — это не адрес никакой переменной.
Прежде чем использовать указатель, который может быть NULL, проверьте его через if:
#include <stdio.h>
int *find_negative(int *numbers, int count) {
for (int i = 0; i < count; i++) {
if (numbers[i] < 0) {
return &numbers[i];
}
}
return NULL;
}
int main(void) {
int week[] = {3, 1, -2, 0, -4};
int *frost = find_negative(week, 5);
if (frost != NULL) {
printf("Первый мороз: %d\n", *frost);
} else {
printf("Мороза нет\n");
}
return 0;
}Программа выведет:
Первый мороз: -2Зачем проверять? Чтение или запись через NULL — это баг, потому что там нет никакой переменной. На компьютере такая программа обычно аварийно завершается, часто с сообщением Segmentation fault. Здесь, в браузере, чтение через NULL останавливает программу с ошибкой RuntimeError: unreachable, той же, что и при выходе индекса за конец массива. Она не говорит, что пошло не так, поэтому, увидев её в программе с указателями, поищите указатель, который может быть NULL.
NULL — ещё и хорошее начальное значение для указателя, которому пока не на что указывать: int *found = NULL;. Указатель вообще без значения, int *p;, здесь вызывает ошибку компиляции, как только вы его используете (variable 'p' is uninitialized when used here), как и любая переменная без значения.
Ещё одно правило для функций, возвращающих указатель: возвращайте только адреса того, что ещё существует, когда функция закончится, например элементов полученного ею массива. Локальные переменные функции исчезают, когда она заканчивается, поэтому никогда не возвращайте их адреса.
Задание
- 1
В массиве
row1— занятое место, а0— свободное. Программа не компилируется: когда свободных мест нет, функцияfind_free_seatзаканчивается безreturn. Добавьтеreturn NULL;в её конец, после цикла. Тогда программа дважды выведетЗабронировано. - 2
В кинотеатр приходит третий человек. В
mainвызовитеbook_seat(row, 4);в третий раз. Программа дважды выведетЗабронировано, а затем остановится с ошибкойRuntimeError: unreachable: ряд заполнен, поэтомуfind_free_seatвернулаNULL, аbook_seatпопыталась записать через него. Исправьтеbook_seat: еслиseatравенNULL, она выводитМест нети ничего не меняет; иначе бронирует место, как раньше. Программа выведетЗабронировано,ЗабронированоиМест нет.