Получить предупреждение о смещении влево отрицательного числа

Я пытаюсь создать предупреждение о неопределенном поведении при смещении влево отрицательного числа. Согласно ответу это сдвиг влево отрицательного числа в C не определен.

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behaviour is undefined.

Я пытаюсь понять, почему я не получаю предупреждение с этим кодом: x << 3

gcc-Стена (версия 9.1.0)

int main ()
{
    int x= -4, y, z=5;
    y = z << x;
    y = x << 3;
    return y;
}

Отдельно стоит отметить, что меня также не предупреждают о смещении влево на отрицательное число. z << x

почему я не получаю предупреждение с этим кодом: x << 3? Поскольку левый операнд x может быть знаковым или беззнаковым. Стандарт не ограничивает, что x должен быть только беззнаковым типом, т. е. он может быть подписанным, т. е. может содержать отрицательные значения.
Achal 29.06.2019 19:36

Но вы делать получаете предупреждение за y = z << -4; (ваше последнее предложение). Компилятор пытается помочь, но не запускает код, чтобы определить, всегда ли x отрицательно. Это может быть даже не знать, если значение для x является вводом пользователя во время выполнения.

Weather Vane 29.06.2019 19:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
759
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

Если авторы Стандарта намеревались внести какие-либо изменения в обработку операций сдвига влево в случаях, когда поведение C89 имело смысл, в опубликованном документе «Обоснование» должно быть какое-то упоминание об этом изменении. Ничего подобного нет. Единственный вывод, который я могу сделать из такого упущения, состоит в том, что переход от «Вести себя определенным образом, который почти всегда имеет смысл» к «Вести себя таким образом, если разработчик считает, что это имеет смысл, и в противном случае обрабатывать код любым другим способом». способ, который разработчик считает нужным», не рассматривался как достаточное изменение, чтобы оправдать комментарий. Тот факт, что авторам Стандарта не удалось предписать поведение, никоим образом не означает, что они не ожидали, что реализации общего назначения с дополнением до двух не будут продолжать обрабатывать такие сдвиги, как они всегда это делали, и что программисты не должны иметь право на аналогичные ожидания.

Вопрос не спрашивает, почему сдвиг влево отрицательного значения не определен или какова история. Он спрашивает, почему компилятор не выдает предупреждающее сообщение для z << x или x << 3, когда x отрицательное (несмотря на то, что оно выдает для z << -4 или -4 << 3).

Eric Postpischil 30.06.2019 00:55

@EricPostpischil: многие компиляторы определяют больше вариантов поведения, чем тот минимум, который описан в стандарте. Поведение было определено в C89, и авторы gcc, вероятно, думали, что проще обрабатывать действие как определенное, независимо от того, находится ли компилятор в режиме C89 или C99, чем обрабатывать его только как определенное в первом случае.

supercat 30.06.2019 06:09
Ответ принят как подходящий

В 5 << -4 как GCC 9.1.0, так и Apple LLVM 10.0.1 с clang-1001.0.46.4, ориентированные на x86-64, выдают предупреждающее сообщение («количество сдвигов влево отрицательное» для GCC и «счетчик сдвигов отрицательное» для LLVM- лязг). В -4 << 3 GCC не выдает предупреждения, а LLVM-clang выдает («сдвиг отрицательного значения не определен»).

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

Когда операнд не является константой, как в z << x и x << 3, мы можем предположить, что компилятор не видит, что операнд отрицателен, поэтому он не выдает предупреждение. В общем, эти выражения могут иметь определенное поведение: если x неотрицательно и находится в подходящих пределах (не больше ширины z в первом случае и не настолько велико, чтобы x << 3 переполнялось во втором случае), поведение определено (за исключением возможности переполнения z << x). Стандарт C не говорит, что сдвиги влево с типами, которые мая имеют отрицательные значения, то есть, типы со знаком, не определены, просто сдвиги влево с отрицательными значениями не определены. Поэтому для компилятора было бы ошибкой выдавать предупреждающее сообщение всякий раз, когда x был подписанным типом в таком выражении, как z << x или x << 3.

Конечно, учитывая int x = -4 и отсутствие каких-либо изменений в x между этой инициализацией и выражениями z << x и x << 3, компилятор может сделать вывод, что в данном конкретном случае сдвиги не определены, и выдать предупреждение. Этого не требует стандарт, и неспособность компилятора сделать это просто зависит от качества реализации компилятора.

Кажется, я упомянул, что использовал «gcc -Wall». Я нет упомянул версию. Это 9.1.0. Что касается x <<3, я думаю, что что-то вроде распространения копирования поможет компилятору понять, что x было отрицательным значением. Я только что заметил, что для gcc компилятор даже не предупреждает меня о -4 << 3, хотя он предупреждает меня о 5 << -4. godbolt.org/z/i34yM2

abjoshi - Reinstate Monica 30.06.2019 02:24

Кроме того, учитывая, что компилятор не предупреждает, что x << 3 не определено (что в данном случае определенно так), могу ли я предположить, что он не будет выполнять скрытую оптимизацию за моей спиной (предполагая неопределенное поведение)?.

abjoshi - Reinstate Monica 30.06.2019 02:31

@abjoshi: Нет, отсутствие предупреждения компилятора не означает, что вы можете предположить, что компилятор не будет «оптимизировать» код.

Eric Postpischil 30.06.2019 02:33

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

abjoshi - Reinstate Monica 30.06.2019 02:39

@abjoshi: отсутствие предупреждения компилятора не означает, что компилятор не заменит полностью неопределенное поведение в вашем коде чем угодно.

Eric Postpischil 30.06.2019 02:50

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

supercat 01.07.2019 19:22

Краткое объяснение того, что компилятор не выдает предупреждения в таких случаях, заключается в том, что это не требуется.

Определение «не определено» в стандарте также прямо указывает «не требуется диагностика». Это означает, что стандарт не требует реализации для выдачи диагностики в таких случаях. Причина этого в том, что технически компиляторы могут быть не в состоянии обнаружить все случаи неопределенного поведения в течение разумного времени или (в некоторых случаях) вообще. Некоторые случаи могут быть обнаружены только во время выполнения. Компиляторы представляют собой сложные фрагменты кода, поэтому, даже если человек может легко распознать проблему, компилятор может этого не сделать (с другой стороны, компиляторы также обнаруживают проблемы, которые люди не могут легко найти).

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

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

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

Следующее требование для выдачи предупреждения состоит в том, что пользователь компилятора должен выбрать отображение предупреждений. Благодаря яростному лоббированию поставщиков разработчиками - большинство современных компиляторов настроены ПО УМОЛЧАНИЮ, поэтому они выдают относительно небольшое количество предупреждений. Поэтому разработчикам, которым требуется как можно больше помощи от компилятора, необходимо явно включить его. Например, используя опции -Wall с gcc и clang.

Другие вопросы по теме

Как я могу заставить GCC/Clang предупреждать об использовании неинициализированных членов?
Растровое предупреждающее сообщение: In cbind(m[i, ], vals) : количество строк результата не кратно длине вектора (arg 2)
Предупреждения Python pytest при запуске теста
Игнорировать предупреждение Pandas KeyError: значение не в индексе
Как избежать предупреждения о «клонировании ресурсов» в Chef 12 при использовании ресурса, имя которого нельзя изменить
Восстановление NuGet — как заставить его рассматривать предупреждения как ошибки?
Как исправить «присваивание делает указатель из целого числа без приведения [-Wint-conversion]»? Unix-программа на С
GCC -Wall отображает предупреждение «неиспользуемый параметр» при использовании uin64_t в сочетании с -m32 и средой тестирования Unity
Java — Intellij IDEA предупреждает о NPE в лямбда-методе enum getter
React Native: предупреждение о реквизитах, недопустимое значение реквизита