Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Для новичков > Разыменовывание нулевого указателя приводит к неопределённому поведению |
Автор: Thunderbolt 17.2.2015, 10:11 | ||||||
Напомню историю обсуждений Все началось со http://www.viva64.com/ru/b/0299/ о проверке ядра Linux с помощью анализатора PVS-Studio. Но сама проверка ядра тут ни причём. Дело в том, что в статье я привёл следующий фрагмент из кода Linux:
Я назвал этот код опасным, так как посчитал, что здесь имеет место http://www.viva64.com/ru/t/0066/. По этому поводу я получил много возражений от читателей и даже одно время был готов поддаться на их убедительные речи в письмах и комментариях. Например, в качестве доказательства корректности кода приводили устройство макроса http://www.viva64.com/go.php?url=1481, который часто реализован так:
Здесь имеет место разыменование нулевого указателя, но код успешно работает. Были и другие письма с рассуждениями того, что раз нет доступа по нулевому указателю, то нет и проблемы. Хотя я и доверчивый, но стараюсь проверять информацию. Я начал разбираться с этой темой и в результате написал небольшую статью: "http://www.viva64.com/ru/b/0301/". По всему выходило, что я был прав. Так писать нельзя. Однако я не смог окончательно обосновать свою позицию и привести нужные ссылки на стандарт. После статьи вновь последовали письма с возражениями, и я понял, что надо разобраться с данной темой окончательно. Я обратился с вопросом к экспертам, чтобы узнать их мнение. Эта статья является их обобщенным ответом. О языке Си Выражение '&podhd->line6' является неопределенным поведением в языке C в том случае, если 'podhd' - нулевой указатель. Вот что говорится про оператор взятия адреса '&' в стандарте C99 (Раздел 6.5.3.2 "Операторы взятия адреса и разыменовывания"): Операнд унарного оператора & должен быть либо указателем функции, либо результатом оператора [] или унарного оператора *, либо lvalue-выражением, указывающим на объект, который не является битовым полем и не содержит в объявлении спецификатора регистрового класса памяти. Выражение 'podhd->line6' однозначно не является указателем функции, результатом оператора [] или *. Это как раз lvalue-выражение. Однако, когда указатель 'podhd' равен нулю, выражение не указывает на объект, поскольку в Разделе 6.3.2.3 "Указатели" сказано следующее: Если константа нулевого указателя приводится к типу указателей, то результирующий указатель, называемый нулевым, гарантированно будет не равен указателю на любой объект или функцию. Если "lvalue-выражение не указывает на объект при своем вычислении, возникает неопределенное поведение" (Стандарт C99, Раздел 6.3.2.1 "Lvalue-выражения, массивы и указатели функций"): lvalue - это выражение объектного типа или неполного типа, отличного от void; если lvalue-выражение не указывает на объект при своем вычислении, возникает неопределенное поведение. Ещё раз кратко: Когда оператор -> был применен к указателю, его результатом стало lvalue-выражение, для которого не существует объекта, и в результате мы имеем дело с неопределенным поведением. О языке Си++ В языке С++ всё обстоит точно также. Выражение '&podhd->line6' является неопределенным поведением в языке C++ в том случае, если 'podhd' - нулевой указатель. С толку немного сбивает дискуссия на WG21 (http://www.viva64.com/go.php?url=1484), на которую я ссылался в предыдущей статье. Там настаивают, будто бы такое выражение не является неопределенным поведением. Однако никто так и не нашел никаких правил в стандартах C++, которые разрешали бы использовать "poldh->line6", когда "polhd" - нулевой указатель. Указатель "polhd" нарушает основное ограничение (Раздел 5.2.5/4, второй пункт в списке) о том, что он должен указывать на объект. Ни один объект в C++ не может иметь адреса nullptr. Итого
Этот код является некорректным в языке Си и Си++, если указатель podhd равен 0. Если указатель равен 0, то возникает неопределённое поведение. То, что программа может работать, является везением. Неопределённое поведение может проявить себя, как угодно. В том числе, программа может работать так, как хотел программист. Это один из частных случае, но не более того. Так писать нельзя. Указатель должен быть проверен до разыменования. Разное в дополнение
Благодарности В подготовке статьи мне помогли эксперты, сомневаться в компетенции, которых нет повода. Я благодарен за помощь в написании статьи следующим людям:
Дополнительные ссылки
|
Автор: TarasProger 12.8.2015, 19:56 | ||||
Разыменование нулевого указателя и не должно быть ни безопасным, ни определённым. Взять хотыбы этот код из стартового поста:
|