Если я читаю спецификацию ISO C ++ (разделы 5.8.2 и 5.8.3) справа, сдвиг вправо с отрицательными знаковыми типами зависит от реализации, а поведение сдвига влево не определено.
Поэтому я хотел бы найти операции сдвига для подписанных типов в нашем унаследованном исходном коде, который мы компилируем с помощью g ++ 4.8.2.
К сожалению, я не смог найти такой вариант в руководство.
Я могу, например, скомпилировать этот код с «g ++ -Wall -Wextra -pedantic» без предупреждения:
int si = -1;
int left = si << 1; // -2 (multiplication by 2, sign is preserved)
int right = si >> 1; // -1 (no change, only 1s)
Может кто-нибудь сказать мне, если есть такое предупреждение, и если нет, почему gcc не заботится об этом?
насколько мне известно НКУ не предоставляет такую возможность. Стандарт, как вы цитировали, гласит:
N3690 — §5.8.3
Значение E1 >> E2 — это биты E2, сдвинутые вправо E1. Если E1 имеет
тип без знака или если E1 имеет тип со знаком и неотрицательное значение,
значение результата является неотъемлемой частью отношения
E1 / 2E2. Если E1 имеет тип со знаком и отрицательное значение, результирующий
значение определяется реализацией.
а это значит делать
int si = -1;
int right = si >> 1;
может или не может дать -1 в результате. Это реализация определена. И это означает компилятор не обязан выдавать предупреждение лайк «другие компиляторы могут сделать это по-другому».
Некоторые из причин этого выбора следуют.
Оригинал К&R отрывок говорит:
«Смещение вправо без знака, заполняет освобожденные биты 0. Право
сдвиг числа со знаком заполняется знаковыми битами (арифметическое смещение)
на некоторых машинах, таких как PDP-11, и с 0 битами (логический сдвиг)
на других. «
это означает, что операция зависит от архитектуры. Причиной этого является то, что некоторые архитектуры быстры в выполнении одного из двух, но не обоих.
Эта причина плюс тот факт, что полезность сдвигов с расширенными знаками является незначительной, заставило стандарт сделать выбор, оставив его «определенным для реализации». От «полезность знаковых расширенных смен«Я имею в виду, что смещение вправо арифметически целого числа со знаком минус не работает как положительный аналог (потому что потеря 1 справа уменьшает отрицательное число, то есть больше по модулю)
+63 >> 1 = +31 (integral part of quotient E1/2E2)
00111111 >> 1 = 00011111
-63 >> 1 = -32
11000001 >> 1 = 11100000
Рекомендации для дальнейшего чтения:
https://stackoverflow.com/a/1857965/1938163
http://www.ccsinfo.com/forum/viewtopic.php?t=45711
https://isocpp.org/std/the-standard
Редактировать: если вышеупомянутое не решает проблему (то есть код действителен, почему компилятор должен предупредить об этом?) Я предлагаю второе решение: AST matchers
Как описано здесь: http://eli.thegreenplace.net/2014/07/29/ast-matchers-and-clang-refactoring-tools/ Вы можете написать некоторый код, чтобы быстро идентифицировать все места с правыми сдвигами и со знаком в вашей программе.
Думайте об этом какнаписание моей небольшой проверки статического анализа одной задачи».
Изменить 2: вы также можете попробовать другие инструменты статического анализа, Clang имеет -fsanitize = сдвиг вариант, который может работать для вас. AFAIK также для НКУ они внедряли дезинфицирующее средство с неопределенным поведением, которое могло помочь диагностировать эти ошибки. Я не следил за историей, но я думаю, вы могли бы также попробовать.
Документация GCC Вот.
Похоже, что ответ «нет», нет никакого предупреждения о том, что вы хотите.
Я не знаю почему, но я предполагаю, что существует так много кода, который использует это, что было бы слишком шумно. C может не определять арифметические сдвиги, но в настоящее время практически все процессоры делают это одинаково, поэтому большинство людей считают, что оно определено именно так.