GCC предупреждает без приведения, в то время как MSVC предупреждает с помощью приведения, почему? - PullRequest
0 голосов
/ 04 декабря 2018

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

#define THE_INTTYPE  signed short

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

typedef THE_INTTYPE rInt;
enum { /* assume */ char_bit = 8 };
rInt const rMin = ((rInt)1 << (sizeof(rInt) * char_bit - 1));

На этом этапе MSVC2017 на уровне предупреждения 4 не выдает предупреждение, но gcc -pedantic -Wall выдает warning: overflow in implicit constant conversion [-Woverflow].Я понимаю, почему он жалуется, хотя.Это потому, что я перешагнул через MSB со знаком целого числа.

Вау, целочисленное значение неявно преобразуется из положительного (до сдвига) в отрицательное (после сдвига)!Похоже на ошибку, лучше предупреждает его.- GCC подумал

Я утверждаю свое намерение, добавив (rInt) приведение, чтобы оно стало (rInt)((rInt)1 <<....GCC больше не жалуется.MSVC2017, однако, неожиданно выдает предупреждение: warning C4310: cast truncates constant value.

Наконец-то мне удалось «исправить» его, приведя 1 к uintmax_t.

/* Perfect no warning code */
rInt const rMin = (rInt)((uintmax_t)1 << (sizeof(rInt) * char_bit - 1));

Вопрос в том, почему?Если MSVC2017 является более строгим, чем GCC, почему он выдает предупреждение, если и только если я добавил приведение? Почему MSVC2017 решил сделать предупреждение только после того, как я добавил актерский состав? Я что-то здесь пропустил?

((signed short)1) -> 0000 0000 0000 0001
<< (2 * 8 - 1) -> 1000 0000 0000 0000 // GCC warns, understandable, MSVC no warn
(signed short)((signed short)1 ... // GCC no warn, MSVC warns, why?

1 Ответ

0 голосов
/ 06 декабря 2018

Итак, я снова прочитал официальную документацию с предупреждением C4310 и нашел ключевое слово: cast и truncate .

Очевидно, MSVC считает, что преднамеренное приведение является непреднамеренным усечением .Как так?Давайте посмотрим на официальный пример.

long int a;
a = (char) 128;   // C4310, use value 0-127 to resolve

Для MSVC char подписано по умолчанию.Поскольку значение signed char колеблется от -128 до 127, когда компилятор увидел, что кто-то пытается преобразовать 128 как char, что, несомненно, приведет к совершенно другому значению -128, компилятор выдаст предупреждение C4310, посколькуприведение выглядит как ошибка.

Однако, если вы удалите приведение и непосредственно назначите значение char вместо этого:

char c = 128; // warning?

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

Так что, если вы допустите ошибку, инициализируйте переменную signed положительным целочисленным значением, которое может быть представлено только unsigned, MSVC будетне предупреждаю вас.MSVC предупредит вас, только если вы инициализируете значением, которое не может обработать даже версия unsigned типа данных.

char c = 255; // no warning
char c = 256; // warning: truncation from 'int' to 'char'

Или если вы действительно хотите, чтобы MSVC проверил это за вас, вам нужно привести.

char c = (char)128; // MSVC emits C4310

Таким образом, я считаю C4310 бесполезным и, возможно, вредным.Кажется, это поощряет программиста на C не разыгрывать вещи, которые должны быть разыграны.Потому что, если вы разыгрываете, это предупреждает.Если вы не сыграете, это не предупредит.Так зачем разыгрывать, когда вы можете уйти без кастинга?Это смешно ИМХО.

В GCC ситуация меняется на противоположную.В ролях?Нет предупрежденияНет актеров?Издайте предупреждение.Это так просто.

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


Bouns

Найдите минимальное значение, которое может содержать произвольный целочисленный тип со знаком безпредупреждение от обоих компиляторов, без uintmax_t.

typedef short rInt;
rInt const rIntMin = (rInt)-1 << (sizeof(rInt) * CHAR_BIT - 1);

Как это работает?Возьмем, например, 16-битный код, код превращается с 1111 1111 1111 1111 в 1000 0000 0000 0000.Поскольку целочисленное значение является отрицательным до сдвига битов, а также отрицательным после сдвига битов, операция считается безопасной, поэтому предупреждение от обоих компиляторов отсутствует.

...