Есть -1 ›› 5; неуказанное поведение в C?

C11 §6.5.7, абзац 5:

Результатом E1 >> E2 является E1 сдвиг вправо E2 битовых позиций. Если E1 имеет беззнаковый тип или если E1 имеет знаковый тип и неотрицательное значение, значение результата является неотъемлемой частью частного E1 / 2*^E2. Если E1 имеет знаковый тип и отрицательное значение, результирующее значение определяется реализацией.

Но в справочном документе viva64 говорится:

int B;
B = -1 >> 5; // unspecified behavior

Я запускал этот код на GCC, и он всегда выдает результат -1.

Итак, стандарт говорит, что «Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией», но в этом документе говорится, что -1>>5; является неопределенным поведением.

Итак, -1>>5; неопределенное поведение в C? Что правильно?


person msc    schedule 16.10.2017    source источник
comment
Если вы пытаетесь написать переносимый код, различие между определенным реализацией и неопределенным не очень существенно, поэтому средства проверки кода относятся к ним одинаково.   -  person Barmar    schedule 16.10.2017
comment
Это определяется реализацией.   -  person chux - Reinstate Monica    schedule 16.10.2017
comment
@chux Итак, этот документ неверен ??   -  person msc    schedule 16.10.2017
comment
Этот документ цитирует стандарт в своем объяснении.   -  person Barmar    schedule 16.10.2017
comment
Это не обман. Данный дубликат спрашивает, почему числа со знаком со сдвигом вправо работают именно так. Этот вопрос касается разницы между неопределенным и определенным реализацией. Повторное открытие.   -  person dbush    schedule 16.10.2017
comment
Спецификация всегда верна   -  person RecursiveExceptionException    schedule 17.10.2017
comment
Почему добавлена ​​награда? У этого вопроса уже много просмотров и много положительных ответов.   -  person M.M    schedule 24.08.2018
comment
Мне очень хотелось заменить gcc или бит-манипуляция с помощью language-lawyer. Первые два не важны для вопроса, в то время как последнее имеет значение.   -  person Nominal Animal    schedule 27.08.2018


Ответы (4)


Оба верны. Поведение, определяемое реализацией, - это особый тип неопределенного поведения.

Ссылаясь на раздел 3.4.1 стандарта C который определяет "поведение, определяемое реализацией":

1 поведение, определяемое реализацией

неуказанное поведение, где каждая реализация документирует, как сделан выбор

2 ПРИМЕР. Примером поведения, определяемого реализацией, является распространение старшего бита, когда целое число со знаком сдвигается вправо.

Из раздела 3.4.4, определяющего «неопределенное поведение»:

1 неопределенное поведение

использование неуказанного значения или другое поведение, когда настоящий международный стандарт предоставляет две или более возможности и не налагает дополнительных требований, что выбирается в любом случае

2 ПРИМЕР Примером неопределенного поведения является порядок, в котором оцениваются аргументы функции.

Что касается GCC, вы всегда получите один и тот же ответ, потому что операция определяется реализацией. Он реализует сдвиг вправо отрицательных чисел через расширение знака

Из документации GCC:

Результаты некоторых поразрядных операций с целыми числами со знаком (C90 6.3, C99 и C11 6.5).

Побитовые операторы воздействуют на представление значения, включая биты знака и значения, где бит знака считается непосредственно над битом значения самого высокого значения. Знаковый >> действует с отрицательными числами путем расширения знака.

Как расширение языка C, GCC не использует широту, указанную в C99 и C11, только для обработки определенных аспектов подписанного << как неопределенного. Однако -fsanitize=shift-fsanitize=undefined) диагностируют такие случаи. Также диагностируются там, где требуются постоянные выражения.

person dbush    schedule 16.10.2017
comment
Это не совсем правильно: поведение, определяемое реализацией, - это особый тип неопределенного поведения. Если поведение определяется реализацией, стандарт указывает, что реализация должна определять и задокументировать его, поэтому оно не может быть неопределенным. Неопределенное поведение предназначено для ситуаций, когда реализация может свободно выбирать, но не должна документировать поведение или делать его согласованным. - person R.. GitHub STOP HELPING ICE; 17.10.2017
comment
@R .. За исключением определения поведения, определяемого реализацией, на самом деле используются точные слова "неопределенное поведение". Я полагаю, что требование документации не считается наложением дополнительных требований, которые выбраны. - person aschepler; 17.10.2017
comment
Я бы интерпретировал определение поведения, определяемого реализацией, которое вы указали, как исключающее неопределенное поведение, то есть значение, как для неопределенного поведения, но также и с этим требованием. В конце концов, определение неопределенного поведения включает в себя никаких дополнительных требований, которые выбираются, однако поведение, определяемое реализацией, требует, чтобы выбор был задокументирован реализацией, поэтому он не соответствует определению неопределенного поведения. - person M.M; 19.10.2017
comment
Поведение @ M.M, определяемое реализацией, говорит: документы о том, как делается выбор, поэтому реализация может задокументировать его как случайный и соответствующий стандарту, и это неотличимо от любого другого неопределенного поведения. - person Antti Haapala; 27.08.2018
comment
Я предоставляю дополнительные доказательства, DR на C89 :) - person Antti Haapala; 27.08.2018
comment
@AnttiHaapala: Я этого не видел. Тем не менее, в Обосновании C89 действительно говорится, что, хотя несовершенная реализация, вероятно, могла бы создать программу, которая удовлетворяет этому требованию, но все же окажется бесполезной, Комитет C89 посчитал, что такая изобретательность, вероятно, потребует больше работы, чем создание чего-то полезного. Таким образом, на большинство вопросов о том, позволяет ли Стандарт соответствующей реализации вести себя каким-то тупым образом, следовало бы ответить. Стандарт, вероятно, позволил бы реализации, отвечающей качеству мусора, но соответствующей требованиям, вести себя таким образом. Так? Жаль, что они не были. - person supercat; 27.08.2018
comment
@AnttiHaapala: Это не значит, что все подобные вопросы касаются поведения, которое в целом было бы глупым. Большинство из них касается поведения, которое в одних случаях было бы разумным, а в других - тупым. К сожалению, авторы Стандарта, отвечая на такие вопросы, не разъясняют, что Стандарт часто разрешает вести себя определенным образом как в тех случаях, когда это глупо, так и в тех, где это разумно; они ожидают, что люди, пишущие, стремящиеся написать качественные реализации для определенной цели, смогут судить, какие случаи есть какие. - person supercat; 27.08.2018
comment
Поскольку очевидно, что эта штука возвращается к жизни или, по крайней мере, к нежити, я склонен согласиться с Ашеплером. Естественное толкование неопределенного поведения, где [...] в определении поведения, определяемого реализацией, заключается в том, что поведение, определяемое реализацией, является подкатегорией неопределенного поведения. Например, если я говорю, что гонка в мешках - это гонка, в которой у каждого участника ноги и нижняя часть туловища находятся в большом мешке, я имею в виду, что гонка в мешках - это особый вид гонки, а не то, что гонка в мешках на самом деле не гонка, но только как один. - person John Bollinger; 27.08.2018

«Неопределенное поведение» и «определенная реализация» не противоречат друг другу. Это просто означает, что стандарт C не определяет, что должно происходить, и что различные реализации могут делать то, что они считают «правильным».

Запуск его несколько раз на одном компиляторе и получение одного и того же результата означает только то, что этот конкретный компилятор согласован. Вы можете получить разные результаты на другом компиляторе.

person skrrgwasme    schedule 16.10.2017
comment
Ни один термин не является подмножеством другого. Если действие вызывает неопределенное поведение, реализации должны выбирать из конечного набора вариантов (например, x()+y() должен вести себя так, как будто он либо полностью оценивает x(), а затем оценивает y(), либо полностью y(), а затем x(); это единственные два варианта) . Если действие вызывает поведение, определяемое реализацией, реализации должны документировать конкретное поведение, но могут делать практически все, что захотят, если они это документируют. - person supercat; 17.10.2017
comment
@supercat указывает документ как делается выбор, а не документ какое поведение было выбрано. - person Antti Haapala; 27.08.2018
comment
@AnttiHaapala: Я не думаю, что «Определение реализации» предназначено как приглашение для реализаций просто сказать: «Эта реализация выбирает среди этих вариантов поведения непредсказуемо». Стандарт не предъявляет жестких требований к качеству документации по реализациям, но я думаю, что очевидным выводом будет то, что только некачественные реализации не смогут сказать что-то полезное. - person supercat; 27.08.2018
comment
@supercat, я не вижу, что в определении неопределенного поведения требует, чтобы поведение выбиралось из конечного набора вариантов, если только компьютеры не могут представлять только конечное число различных состояний. Два или более не передают мне конечности. - person John Bollinger; 27.08.2018
comment
@JohnBollinger: места в Стандарте, где я заметил, что используемый термин описывает вещи, которые будут иметь конечное число вариантов, например порядок, в котором выполняются различные операции, или значения, оставленные в байтах заполнения структуры при предшествующем члены написаны. Для Стандарта могло бы быть возможным и уместным использовать этот термин в случаях, когда могло быть бесконечное количество возможных вариантов поведения, которые все были бы очень похожи (например, говоря, что если реализация заранее определяет определенный макрос, поведение целочисленного переполнения будет ограничиваться получением ценности ... - person supercat; 27.08.2018
comment
... который ведет себя как любое математическое целое число, которое соответствует правильному значению, изменяет диапазон целочисленного типа. Так как таких значений было бы счетное бесконечное число, следовательно, было бы счетно бесконечное число возможных вариантов поведения). Я не знаю, где бы Стандарт действительно относился к неопределенному поведению, однако, где он также не определял бы конечное количество возможностей. Есть ли такое место, чтобы я не заметил? - person supercat; 27.08.2018

Поведение, определяемое реализацией, является подклассом неопределенного поведения, то есть поведения, которое не определено стандартом.

Отчет о дефектах № 154 - C89 спросил комитет, каковы ограничения для поведение, определяемое реализацией; комитет отвечает, что реализация может определять любое поведение, которое она хочет, и это не обязательно должно быть постоянным.

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

person Antti Haapala    schedule 27.08.2018
comment
Стандарт избегает использования термина «поведение, определяемое реализацией» в тех случаях, когда любое значительное количество качественных реализаций, как ожидается, произнесет случайный ответ. Порог между UB и IDB, как правило, заключается в том, предусмотрели ли авторы Стандарта какие-либо правдоподобные ситуации, когда какая-либо реализация может быть неспособна указать что-либо полезное. Рассмотрим поведение -1<<x. Он был определен в C89, но стал UB в C99, хотя почти все реализации вели себя одинаково. Если IDB было приглашением сказать наугад, то почти все формы ... - person supercat; 27.08.2018
comment
... из UB может так же легко быть IDB, но возможность того, что может существовать какая-то реализация, в которой может быть трудно задокументировать последовательное поведение для -1<<x, кажется адекватным оправданием для того, чтобы такое действие запускало UB, а не рассматривало его как IDB [в обосновании C99 это изменение даже не упоминается, не говоря уже о его причинах]. - person supercat; 27.08.2018

Я не получил ни одного из ответов на данный момент. В стандарте C четко указано, что сдвиг отрицательного числа вправо - это поведение, определяемое реализацией. Это не неопределенное поведение, которое означает нечто иное. Как вы правильно цитируете (C17 6.5.7 §5):

Результат E1 >> E2 - E1 сдвинутые вправо битовые позиции E2. / - /
Если E1 имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией.

Это означает, что компилятор должен задокументировать его поведение. Период.

На практике: документ должен указывать, использует ли компилятор арифметический сдвиг вправо или логический сдвиг вправо.


Это противоположно неопределенному поведению, которое является специфическим для реализации поведением, которое не требуется документировать. Неопределенное поведение используется в двух случаях:

  • Когда поведение компилятора может быть секретом реализации, который поставщик компилятора не должен раскрывать своим конкурентам.
  • Когда компилятор не может документировать, как работают основные детали, такие как ячейки памяти ОС и ОЗУ.

Например, компилятору не нужно документировать порядок оценки в коде следующим образом:

a  = f1() + f2();
a += f1() + f2();

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

Точно так же компилятору не нужно документировать то, что печатает этот код:

int a;
int ptr = &a;
printf("%d", *ptr);

a - неопределенное значение, и вывод не определен - на практике вывод зависит от того, что было сохранено в этой конкретной ячейке RAM раньше. То, что мы бы назвали «мусорной стоимостью». (Прежде чем кричать «UB», прочтите (Почему) используется неопределенное поведение неинициализированной переменной?).

person Lundin    schedule 30.08.2018