Подтвердить что ты не робот

Есть ли предупреждение GCC, которое обнаруживает операции сдвига бит на подписанных типах?

Если я правильно прочитал спецификацию ISO С++ (разделы 5.8.2 и 5.8.3), правый сдвиг отрицательных подписанных типов является специфичным для реализации и левым сдвигом undefined.

Поэтому я хотел бы найти операции сдвига по подписанным типам в нашем исходном исходном коде, который мы компилируем с помощью 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 не заботится об этом?

4b9b3361

Ответ 1

AFAIK gcc не предоставляет такой опции. В стандарте, как вы сказали, говорится:

N3690 - §5.8.3

Значение E1 → E2 - это позиции E1 с правым сдвигом E1. Если E1 имеет unsigned type или если E1 имеет подписанный тип и неотрицательное значение, значение результата является неотъемлемой частью частного E1/2E2. Если E1 имеет подписанный тип и отрицательное значение, результат значение определяется реализацией.

а это означает выполнение

int si    = -1;
int right = si >> 1;

может привести или не дать -1 в результате. Определено реализация. И это означает, что компилятор не вынужден выдавать предупреждение, как "другие компиляторы могут сделать это другим способом".

Ниже приводятся некоторые причины этого выбора.

В оригинальном отрывке K & R говорится:

"Правое смещение беззнакового числа заполняет освобожденные биты с 0. Вправо смещение знаковой величины будет заполняться знаковыми битами (арифметический сдвиг) на некоторых машинах, таких как PDP-11, и с 0 бит (логический сдвиг) на других."

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

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

+63 >> 1 = +31 (integral part of quotient E1/2E2)
00111111 >> 1 = 00011111
-63 >> 1 = -32 
11000001 >> 1 = 11100000

Ссылки для дальнейшего чтения:

fooobar.com/questions/231815/...

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 = shift, которая может работать на вас. AFAIK также для gcc они внедряли дезинфицирующее средство поведения undefined, которое могло бы помочь диагностировать эти ошибки. Я не следовал этой истории, но я думаю, вы тоже могли бы попробовать.

Ответ 2

Документация GCC здесь.

Ответ выглядит как "нет", нет какой-либо опции предупреждения для того, что вы хотите.

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