Informatikum
@Informatikum
Преподаю робототехнику и информатику в школе.

Что должна вернуть функция NULL, если ничего не найдено?

Вопрос по поводу указателей, а точнее – указателей NULL, т.е. указатель с нулевым содержимым. В Си – это достаточно обычная вещь. Если указатель нулевой, то значит после его проверки на ноль делается вывод о некорректности чего-либо. Например, если при открытии файла FILE* равен NULL, то файл не открылся по каким-то причинам. Мне представляется, что нулевые указатели – это абсолютное зло. Потому что его забывают проверить. Но допустим, у нас есть функция, которая что-то ищет. Например, элемент в массиве. Если она находит, она должна вернуть указатель на элемент. Для этого функция так и определена: int* find (int* массив, int искомое). Но что должна вернуть функция, если ничего не найдено? Ведь возвращаемое значение должно иметь тип int*. Вот в чём вопрос. Прошу дать развёрнутый ответ с пояснением.
  • Вопрос задан
  • 2294 просмотра
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Так... давай-ка немного поправим твое понимание предмета.

В С/С++ существует такой тип даных, как указатель на данные. Его синтаксис выглядит так:
int* value;

Запись буквально означает что 'value' у нас будет хранить адрес на область данных с предположительной типизацией в четырехбайтовое знаковое целое. Внимательно! 'value' хранит только указатель, никаких данных о размере блока этих данных нет, никаких строгих оговорок о типе этих данных нет (только предположение что это 'int').

При формировании переменной оная, обычно, ничем никак не инициализируется. То есть, после определения нашего 'value' в его значении лежит любой немыслимый мусор. Внимательно! Нет никаких способов (кроме как обратиться и словить AV/SIGSEGV) определить что значение 'value' ссылается на правильный адрес. Поэтому этот самый мусор, который в 'value' и содержится, можно спокойно использовать как адрес блока данных и при обращении по этому адресу получить от ОСи по рукам.

Вопрос! Как этого избежать?
Есть очень простой и очень старый выход - определить некую магическую константу, которая точно смогла бы символизировать чистоту указателя (что указатель не одержит адреса). Именно такой константой 'NULL' и является.
Адрес на блок данных может быть абсолютно любым! Он может быть даже 0xA0L. Но если в значении указателя записан 0 (это и есть NULL), значит указатель чист - он не содержит в себе адрес на блок данных.

Итак! NULL - это не зло. NULL - это признак чистоты указателя!

Теперь перейдем к "тонкостям и нюансам нолика"...

Попробуй собрать такой код:
typedef int* p_int;

p_int value = p_int();
printf( "ptr : 0x%08x\n", value );

Вывод в консои будет : "ptr : 0x00000000". О чем это говорит? Это говорит о том, что возвращаемое инициализатором указателя значение (вот эта вот запись: "p_int()") всегда эквивалентно NULL.

Можно было бы с точно таким же холоднокровием написать вот так:
p_int value = NULL;
И все осталось бы по прежнему.

Дело в том, что NULL имеет тип "void*", а этот тип можно преобразовать абсолютно к любому иному указателю.
И NULL самостоятельно приводится к нужному типу в операторе присвоения указателя этого типа. Ноль - он и в Африке ноль.

Попробуй собрать такой код:
int* value = NULL;
delete value;


Если бы 'value' небыл инициализирован, то с превеликой вероятностью оператор delete привел бы к падению приложения. 'delete' воспринял бы мусор как правильный указатель и попробовал бы освободить память по этому указателю, а так как это мусор, ОСь эту попытку забрила бы, выдав программе красную карточку. Вместе с тем, оператор delete спроектирован так, чтобы не обращать внимания на NULL. Оператор просто тихо завершается если видит нулевой адрес.
Вывод: чистота NULL не нуждается в большей очистке! :)

Вот так. Надеюсь, моя простыня текста хоть немного да поможет тебе.

А по вопросу о функции - да возвращай NULL! Так все делают, чем ты хуже? ;)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
GavriKos
@GavriKos
Если функция на выходе должна вернуть указатель на что то, но этого чего то нет - то возвращается NULL в общем случае. Это нормальное поведение для с/с++. Никакого зла тут нет. NULL - это не тип данных, так что int* foo = NULL - корректно. А "забывают проверить" - это признак непрофессионализма.

Если вас так пугают эти проверки - можно поступить чуть чуть по другому. Сделать функцию
bool find(int* array, int value, int* output).
Функция возвращает true если было что то найдено, или false, если небыло. Результат в случае нахождения помещается в output. Если ничего не найдено - Output не трогается.
Ответ написан
Комментировать
kumaxim
@kumaxim
Web-программист
Если значение в массиве на найдено и без него продолжать невозможно, тогда самый лучший вариант
throw new Exeption('Элемент ' + find_element + ' в массиве не найден');

Выше, где ты вызываешь функцию поиска, в try {} catch(...) {} ловишь это исключение и обрабатываешь, иначе в коде ты задолбаешься обрабатывать свой NULL или -1.
Ответ написан
Комментировать
bobrovskyserg
@bobrovskyserg
Функция должна вернуть то, что написано в техзадании.
Я бы в данном случае предложил возвращать -1
Ответ написан
@vilgeforce
Раздолбай и программист
В общем, мнение мое таково: если возвращается указатель и нужно показать вызывающей стороне что его использовать не надо, функция должна вернуть "магическую константу". Например, 0, -1, 0xDEADC0DE и так далее. Поскольку указатель - фактически адрес в памяти, возвращение "левого" адреса при обращении к нему приведет к исключению, а не продолжению работы программы, что есть хорошо. Есть НО: разные операционки по-разному выделяют память, где-то шансы на валидность адреса 0xDEADBEEF сильно больше нуля. Поэтому идеально использовать 0 или -1: такие адреса будут доступны программе с минимальной вероятностью.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы