Объясните целочисленное сравнение с продвижением - PullRequest
13 голосов
/ 27 июня 2019

Я пытаюсь понять, как работает целочисленное продвижение и сравнение в приложении C ++.

#include <cstdint>

int main(void)
{
    uint32_t foo  = 20;
    uint8_t a = 2;
    uint8_t b = 1;
    uint8_t c = 5;

    if(foo == b*c) {}

    if(foo == a) {}

    if(foo == a + c) {}

    if(foo == a + b*c) {}

    return 0;
}

Только для последнего сравнения я получаю предупреждение компилятора: «сравнение между целочисленными выражениями со знаком и без знака [-Wsign-compare]».

Почему это происходит только в последнемдело, а не в других?

Ответы [ 2 ]

4 голосов
/ 27 июня 2019

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

Для бинарных операторов (кроме сдвигов), если повышенные операнды имеют разные типы, дополнительныеприменяется множество неявных преобразований, известных как обычные арифметические преобразования с целью создания общего типа (также доступного через черту типа std :: common_type)

из-за интегралавведите здесь целочисленные преобразования применяется к:

  • Если любой из операндов имеет тип перечисления с заданной областью, преобразование не выполняется: другой операнд и возвращаемый тип должны иметь одинаковые
    type
    • В противном случае, если один из операндов long long, другой операнд преобразуется в long double
    • В противном случае, если любой операнд double, другой операнд преобразуется в double
    • В противном случае, если один из операндов является плавающим, другой операнд преобразуется в плавающее
    • В противном случае,операнд имеет целочисленный тип (потому что bool, char, char8_t, char16_t, char32_t, wchar_t и перечисление с незаданной областью были повышены в этой точке) и применяются интегральные преобразования для получения общего типа, как показано ниже:
    • Если оба операнда подписаны или оба без знака, операнд с меньшим рангом преобразования преобразуется в операнд с большим целым рангом преобразования
    • В противном случае, если ранг преобразования беззнакового операнда большеили равный рангу преобразования подписанного операнда, подписанный операнд преобразуется в тип беззнакового
      операнда.
    • В противном случае, если тип подписанного операнда может представлять все значения беззнакового операнда, беззнаковый операндпреобразуется в тип подписанного операнда. В противном случае оба операнда преобразуются в беззнаковый аналог типа подписанного операнда.

Те же арифметические преобразования применяются к операторы сравнения тоже.

из всего этого можно сделать вывод, поскольку rhs - это все uint8_t, общий тип будет int, а затем, поскольку rhs равен uint32_t, общий тип оператора == будет uint32_t.но по какой-то причине я понятия не имею gcc не делайте последнее преобразование, пока clang делает это.см. преобразование типа gcc для оператора + в godblot Также может случиться, что предупреждение является ложным предупреждением, и преобразование произошло, как это произошло для оператора +.Посмотрите, как clang видит последние if ( cppinsights ):

if(foo == static_cast<unsigned int>(static_cast<int>(a) + (static_cast<int> 
(b) * static_cast<int>(c))))

Обновление:

Я не смог найтиразница в сборке, сгенерированной двумя компиляторами и согласной с @MM, так что, IMO, это ошибка gcc.

0 голосов
/ 28 июня 2019

Это "ошибка" компилятора. Чтобы уточнить это:

  • Как правило, сравнение между знаком и без знака основано на количествах, определенных реализацией (размеры / диапазоны типов). Например, USHRT_MAX == -1 верно для обычных 16-битных систем и false для обычных 32-битных систем. Ответ "забвения" более детально про это говорит.

  • Все ваши примеры кода четко определены и ведут себя одинаково во всех (соответствующих) системах.

Цель этого предупреждения двоякая:

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

Впрочем, в общем. статическому анализу компилятора не так-то просто разобраться в первом случае, не говоря уже о втором, довольно субъективном.

IMO предупреждение, для вашего кода, является ошибкой, потому что код четко определен и не о чем предупреждать.

Лично я не включаю это предупреждение: я знаком с правилами сравнения со знаком без знака и предпочитаю избегать искажения моего кода для подавления предупреждения.

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

GCC имеет тенденцию слишком часто ошибаться в сторону предупреждения, но они находятся в ситуации, когда не могут угодить всем.

...