Страницы

Поиск по вопросам

пятница, 13 декабря 2019 г.

Почему компиляторы не сообщают о неопределенном поведении

#cpp #c


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

Самое страшное не столько в том, что неопредленное поведение вообще допускается стандартом
или что оно достаточно широко распространено. А в том, что в жизни рядовой программист
плохо понимает когда оно вообще возникает. Тем более разочаровывает то, что разработчики
компиляторов не стремятся помочь своему клиенту рядовому программисту хотя могли бы,
христоматийный пример:

int i = 0;
i = ++i + i++;


Вот этот код мой MSVC съедает молча. Есть еще не мало ситуаций когда компилятор мог
бы предупредить программиста.
    


Ответы

Ответ 1



Почему не сообщают? Потому что не обязаны. Тем не менее некоторые компиляторы могут сообщать о некоторых ситуациях, которые могут приводить к неопределённому поведению. Так, если собрать код из вопроса с помощью GCC или clang, Вы получите предупреждение. Есть и другие ситуации, которые покрыты компиляторами, но не все. Для других ситуаций существуют всевозможные статические анализаторы, типа того же PVS Studio, clang-tidy (встроен в CLion & Resharper++) и прочих.

Ответ 2



Предлагаю взглянуть на неопределенное поведение немного под другим углом: в большинстве случаев возникновения неопределенного поведения непосредственно в языковых конструкциях, оно является нарушением со стороны программиста некоторых закрепленных в стандарте требований, без наличия которых эти конструкции вообще не могли бы существовать в нынешнем варианте языка. Другими словами, компилятору прямо позволено рассчитывать, что программист не будет делать некоторых вещей, потому что написание компилятора без такого послабления не представляется возможным. Простой пример: void Inc(int & x) { x += 1; } Тут кусок x += 1; подразумевает целый букет потенциальных неопределенных поведений: x может быть ссылкой на невалидный объект x может быть ссылкой на объект другого типа (ака strict aliasing violation) x может быть ссылкой на валидный объект, но являющийся частью объекта с const квалификатором к значению x будет производится доступ из других потоков при добавлении 1 произойдет целочисленное переполнение Никаких вариантов действий, кроме как предположить, что ничего плохого тут не произойдет, у компилятора нет. Почему язык пришел к такой ситуации - это другой вопрос. Надо полагать, что большая часть проблем такого рода происходит еще из С.

Ответ 3



Это может быть легаси и/или код, заточенный под определенный компилятор (в том числе автоматически сгенерированный). Ведь поведение не определено лишь с точки зрения стандарта, а у компилятора/платформы всегда есть конкретный ответ на любой вопрос.

Комментариев нет:

Отправить комментарий