Pull to refresh

Comments 30

Я бы на цикл статей разбил материал.

Согласен. Много информации, а всего лишь вторник.

Тема-то богатая, но автор решил иначе
Автор не стал приводить примеры, так он решил.
Но вот классический пример Unspecified behavior для языка C.

int a,b,c;
a=b=0;
c = a+++b;
printf(«Value a=%d\n», a);

Автор решил не приводить примеры неопределённого поведения, т.к. они подробно разжёваны, в том числе и на этом сайте. Автор всего лишь попытался показать разницу между undefined и unspecified behavior, о чём честно сказал в первом абзаце.

P.S.: я кстати не понял, где там в примере Unspecified behavior? a == 1, c == 0. Разве нет?
Любопытно. А в каком компиляторе a==0, b==1?
int a,b,c;
a=b=0;
c = a+++b;
printf(«Value a=%d\n», a);

В зависимости от вида компилятора либо a==1, b==0, либо a==0, b==1.

Эта программа не содержит ничего из описанного в статье и в результате её выполнения a всегда равно 1. Выражение a+++b трактуется компилятором соответствующим стандарту как (a++) + b. В С99 это описано в пункте стандарта 6.4, в С++98 — в пункте 2.4:3.
Да, верно. Но всё равно лучше так программы не писать, поскольку можно случайно сделать непреднамеренную ошибку. Правда, это уже по другой теме.
Почитайте про точки следования в программе.
Undefined behavior – Это самый опасный вариант неопределённости. В Стандарте он служит для описания поведения, которое может привести к полностью непредсказуемым последствиям

Дело в том, что описания UB как раз отсутствуют в Стандарте.
Можно было привести определение, которую сразу же узнаёт любой, кто бывал на стековерфлоу или даже википедии – UB вызывается ситуациями, не описанными конкретно в спецификации языка.
Я в курсе про точки следования (как они назывались до C++11, кстати), спасибо.

> Дело в том, что описания UB как раз отсутствуют в Стандарте.
Не согласен. Как раз именно в Стандарте все неопределённые ситуации и описаны в терминах UndB, UnspB и IDB. И все определения там есть, в самом начале.
как они назывались до C++11, кстати

Fun fact, и вправду, благодарю за наводку.
Я стандарт максимум листал (слишком уж сухо), могу, конечно, ошибаться, транслировал когда-то вычитанное на стековерфлоу.
Судя по комменту, у Вас всё в порядке с сями; судя по статье, Вы умеете писать, не вызывая к себе неприязни.
Если Вас не затруднит обуздать слог и расписать полёт мысли, Хабр мог бы пополниться парой занятных плюсовых статей про тот же UB (Да, таких не одна, однако, нередко последующие добавляют что-то новое, либо же разный слог понятен разным группам людей).
Спасибо за лестный отзыв!
Я стараюсь писать о чём-то новом (в том числе и для себя), а не переписывать одно и то же другими словами. Но если узнаю что-то действительно новое про UB — постараюсь написать и об этом.

Вот пример попроще:
foo( bar(), buzz() ); — где не специфицирован порядок вызова bar и buzz. Гарантируется, что оба завершатся до вызова foo.

UFO just landed and posted this here
UFO just landed and posted this here
int c = Div(arr[idx++], arr[idx++]);

В этой строке — неопределённое поведение, поскольку idx модифицируется дважды между точками следования. Выбор не между Div(0, 1) и Div(1, 0), а между запуском ядерных ракет и пробуждением кт-ху.
UFO just landed and posted this here
int c = Div(arr[idx++], arr[idx]);

Здесь по-прежнему есть неопределённое поведение, поскольку выражение не удовлетворяет второй части того же самого требования стандартов C99 6.5:2/C++98 5:4:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.
Да, в C++ до 17 года так же. Но в современном оба примера будут давать лишь неспецифицированный порядок вычисления, и лишь если вторым аргументом окажется 0, то тогда при делении получим неопределённое поведение.
7.6.1.2/8
Здесь нет неопределённого поведения.
И путаете вы этот пример с чем-то типа такого
int i = 0;
i = i++ + ++i;


Если же вы думаете, что три плюса слитно могут быть разобраны по-разному — как ++ и + в одном случае, и + и ++ — в другом, — то вы ошибаетесь. Парсинг стандартизирован, и должен быть так называемым «жадным». Т.е. всегда будет разбираться как ++ и +. Компилятор, который делает иначе — неправильный компилятор и делает неправильный мёд код.
Ситуация, когда код на языке C++ синтаксически валиден, однако его поведение не определено в Стандарте, в русскоязычной литературе часто называют просто неопределённым поведением.

Во-первых, код не просто синтаксически валиден, но и прошёл проверку типов и семантики.
Во-вторых, не знаю какую русскоязычную литературу читали вы, но в той, которую читал я, путаницы нет: неопределённым поведением называют именно undefined behavior.

Лучше (точнее, полнее) было бы просто перевести страницу на cppreference.
Лучше (точнее, полнее) было бы просто перевести страницу на cppreference

cppreference, конечно, популярный ресурс, но всё-таки это просто wiki в интернете. Лучше использовать стандарт.
Лучше использовать стандарт

Для чего? Для написания статьи а-ля «в стандарте есть пункты x, y и z — читайте их, чтобы всё понять»?
Дело в том, что «просто wiki в интернете», книги, статьи и т.п. существуют для того, чтобы 1) учиться и 2) не лазить по перекрёстным ссылкам суконного языка стандарта. Да, конечно, остаётся вопрос доверия, потому первоисточник является непреложной истиной. Но это справочник, а не учебное пособие.
Потому я и говорю, что если было желание написать такую статью, то перевод весьма качественной, на мой взгляд, wiki был бы полезнее. Да, можно было бы озаботиться и проставлением ссылок на стандарт.
В заключение ещё раз напомню, что все вышеописанные термины относятся к синтаксически валидному коду, который будет успешно скомпилирован. Код, невалидный с точки зрения Стандарта, называется ill-formed program.

Вот хотел ещё уточнить, может быть кто-нибудь из присутствующих подскажет. Я так понимаю, что если код не компилируется, то это значит, что он является ill-formed. Однако обратное неверно: код может скомпилироваться, но всё равно являться ill-formed, например, если в коде нарушено ODR. Так ли это? Нет ли ещё каких-нибудь вариантов компилирующегося кода, который при этом будет ill-formed?
И ещё такой вопрос по поводу успешности компиляции: а не может ли UB всё-таки вызвать ошибку компиляции (просто как возможность, не обязательно даже стабильное повторение)? Оно же всё-таки U. Может при развёртывании каких-нибудь шаблонов или макросов, например?
Хочется для себя по полочкам разложить, как эти все круги диаграммы Венна пересекаются, и что есть их пересечения: Implementation-defined behavior, Unspecified behavior, Undefined behavior, ill-formed, некомпилирующийся код.
Многие виды UB вызывают ворнинги в GCC и Clang.
Кстати, сейчас для определения UB во многих случаях можно писать constexpr-функции. По новому Стандарту UB недопустим в constexpr-функциях, поэтому код не скомпилируется.
alenacpp.blogspot.com/2005/08/unspecified-behavior-undefined.html

Это про первые три подробно. Ill-formed по стандарту — это отрицание термина «well-formed» («program that is not well formed»). Well-formed — это «program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule». То есть это в основном то, что компилируется, но бывают исключения типа «ill-formed, no diagnostic required», тогда это по сути ничем не отличается от undefined behaviour, если компилятор все-таки не реализует эту диагностику (но он может, хоть и не обязан — просто на данный случай нет такого требования).
Sign up to leave a comment.

Articles