предупреждение: сравнение целочисленных выражений с разной подписью: 'int' и 'en' {aka 'enum <anonymous>'} - PullRequest
1 голос
/ 05 мая 2020

Имея этот код:

#include <stdio.h>
#include <stdlib.h>

int main (void) {

   typedef enum {bar,baz,last} en;
   en foo = baz;
   for(int i =bar; i<=last ; i++)
      if(i==foo){
         printf("%i\n",i);
      }

   return 0;
}

Почему компилятор заботится о signedness членов перечисления? Они должны быть представлены от 0 и выше (например, bar=0, baz=1, ...), и поскольку они начинаются с 0 и выше, они никогда не могут быть отрицательными, и поэтому нет причин для рассмотрения вопроса о подписи (если я не назначу bar=-1, но не знаю, возможно ли такое). Так почему ошибка?

Ответы [ 4 ]

1 голос
/ 05 мая 2020

Почему компилятор заботится о подписи членов перечисления?

Здесь компилятор заботится не о подписи enum per se , а подпись вещей, которые вы сравниваете.

Когда у вас есть x == y, а x - целое число со знаком, а y - целое число без знака, они не сравниваются напрямую. Правила C говорят, что одно должно быть преобразовано в тип другого. Если целочисленный тип со знаком уже (технически имеет меньший ранг преобразования), чем тип без знака, он преобразуется в тип без знака.

Это преобразование может изменить значение числа, и это может дать результат отличается от того, что вы хотели. Например, -3 < 4u даст false (0), потому что преобразование -3 в unsigned дает большое значение (4294967293 в обычных реализациях C). Или -3 == 4294967293u даст true (1).

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

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

1 голос
/ 05 мая 2020

Компилятор заботится о подписи, потому что все целочисленные типы, включая типы enum, либо подписаны, либо беззнаковые. Для enum точный базовый целочисленный тип определяется конкретной реализацией c. Раздел 6.7.2.2p4 стандарта C гласит:

Каждый перечислимый тип должен быть совместим с char, целочисленным типом со знаком или целочисленным типом без знака. Выбор типа определяется реализацией, но должен быть способен
представлять значения всех членов перечисления
.
Перечислимый тип является неполным до тех пор, пока он не появится сразу после }, которое завершает список объявлений перечислителя и заполняется после этого.

Для g cc, в частности, он определяется следующим образом:

  • Целочисленный тип, совместимый с каждым перечислимым типом (C90 6.5.2.2, C99 и C11 6.7.2.2).

Обычно это unsigned int, если в перечислении нет отрицательных значений , иначе int. Если указано -fshort-enums, то при наличии отрицательных значений это первое из signed char, short и int, которое может представлять все значения, в противном случае это первое из unsigned char, unsigned short и unsigned int, который может представлять все значения.

Для некоторых целей -fshort-enums является значением по умолчанию; это определяется ABI.

Поскольку вы явно не установили значения для enum, значения начинаются с 0 и go оттуда. Предполагая, что вы используете g cc, он выбирает unsigned int в качестве базового типа.

0 голосов
/ 06 мая 2020

Несмотря на то, что проблема небольшая, в ней много всего происходит

typedef enum {bar,baz,last} en;
en foo = baz;
for(int i =bar; i<=last ; i++)
  if(i==foo){

Также ответ @ lundin :
* Константы en равны int
* Объектами типа en являются char, целочисленный тип со знаком или целочисленный тип без знака.

Код имеет 2 функции сравнения с int и en.

i<=last
i==foo

Еще Как я понял, только 2-й предупреждает о «сравнении целочисленных выражений разной подписи: 'int' и 'en'».

В первом случае i<=last: last - это константа en, так что это просто int <= int -> без предупреждения.

Но, как ни странно, если код был unsigned i, i<=last не предупреждает, даже если теперь это целое число выражения с разной подписью ».

Изменение последнего по-прежнему является« целочисленными выражениями с разной подписью », но без предупреждения.

// i==foo
(i&1)==foo

Заключение Предупреждение о« целочисленных выражениях с разной подписью »возникает, когда 2 выполнены условия:

  • Типы разного знака (ракетостроение на вот этот).
  • Компилятор завершает диапазон возможных значений стороны подписанный , а диапазон значений беззнакового не оба находятся в положительном подписанном диапазоне - или что-то в этом роде вот так.

Хотя наш человеческий анализ for(int i =bar; i<=last ; i++) if(i==foo){ printf("%i\n",i); } не видит здесь проблемы сравнения, компилятор ее пропустил.

0 голосов
/ 05 мая 2020

Вы также могли бы сделать typedef enum {bar=-1,baz,last} en; и потенциально получить другой перечислимый тип.

Важно знать, что C дисфункциональна и непоследовательна, когда дело касается типов перечислений. Константы перечисления (bar et c) гарантированно всегда будут иметь тип int, но перечисляемый тип (en) может иметь любое целое число, определяемое реализацией. тип, который соответствует всем константам в списке. Он может быть подписанным или неподписанным. Из стандарта C 6.7.2.2:

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

Вы можете иметь разные типы для разных перечислений в одной программе. Возможно, причина в том, что они хотели сэкономить место. Но это происходит за счет переносимости и недетерминированности c типизации.

Компилятор, вероятно, выдает здесь общее предупреждение, не проверяя, какому внутреннему типу en фактически соответствует. Вы можете проверить себя с помощью макроса C11:

#define type_print(x) _Generic((x),                                  \
                               int:          puts("int"),            \
                               unsigned int: puts("unsigned int"),   \
                               default:      puts("something else"))

type_print(foo);

Это дает мне unsigned int на g cc x86, и поэтому i==foo вызывает предупреждение о преобразовании со знаком и без знака. Лучший способ решить проблему - вместо этого набрать for(en i=bar; ....

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...