C Сравните перечислить с недопустимым значением - PullRequest
0 голосов
/ 30 января 2019

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

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

Я написал следующую небольшую программу, чтобы подвести итог моей проблемы.

Я создаю перечисление E_Number и создаю переменную a со значением -1.Затем я выполняю сравнение на a, чтобы проверить, относится ли оно к диапазону перечисления.

(Я знаю, это действительно странно, но это именно то, что я нашел в исходном коде!)

Я ожидал, что результат говорит мне Не в диапазоне из-заневыполнение первого условия (a >= FIRST_ENUM).Но именно провал второго условия (a < NB_MAX_NUMBER) дал мне правильный результат (см. printf()) ...

Если я разыграю a в (int) в условиях ifЯ получаю исключительные результаты.

Так что же происходит во время казни?Программа рассматривает -1 в качестве другого возможного значения перечисления, которое будет позиционироваться после NB_MAX_NUMBER?Какое правило для операторов > и < для enum?

#include <stdio.h>

#define FIRST_ENUM 0
typedef enum{
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

int main()
{
    E_Number a = -1; 

    if ((a >= FIRST_ENUM) && (a < NB_MAX_NUMBER))
    {
        printf("In Range\n"); 
    }
    else
    {
        printf("Not in Range\n"); 
    }

    printf("1st condition = %s\n", (a >= FIRST_ENUM)?"TRUE":"FALSE"); 
    printf("2nd condition = %s\n", (a < NB_MAX_NUMBER)?"TRUE":"FALSE"); 

    return 0; 
}

gcc program.c

. \ A.exe
Не в диапазоне
1-е условие = ИСТИНА
2-е условие = ЛОЖЬ

Я работаю с компилятором MINGW (gcc (x86_64-win32-seh-rev1, построенный проектом MinGW-W64) 4.9.2)

Ответы [ 3 ]

0 голосов
/ 30 января 2019

В вашем случае компилятор рассматривает E_Number как unsigned int, поскольку все допустимые значения являются беззнаковыми, поэтому -1 считается равным ~ 0u, что> = FIRST_ENUM и

У меня естьто же поведение с gcc version 6.3.0 20170516 (Raspbian 6.3.0-18+rpi1+deb9u1)

pi@raspberrypi:~ $ ./a.out 
Not in Range
1st condition = TRUE
2nd condition = FALSE

Но если я изменю ваши определения следующим образом:

#include <stdio.h>

#define FIRST_ENUM -1
typedef enum{
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

int main()
{
    E_Number a = -2; 

    if ((a >= FIRST_ENUM) && (a < NB_MAX_NUMBER))
    {
        printf("In Range\n"); 
    }
    else
    {
        printf("Not in Range\n"); 
    }

    printf("1st condition = %s\n", (a >= FIRST_ENUM)?"TRUE":"FALSE"); 
    printf("2nd condition = %s\n", (a < NB_MAX_NUMBER)?"TRUE":"FALSE"); 

    return 0; 
}

, то поведение изменится, и enum будет считаться int, и у меня будет:

pi@raspberrypi:~ $ ./a.out 
Not in Range
1st condition = FALSE
2nd condition = TRUE
0 голосов
/ 31 января 2019

Цитата из ISO/IEC 9899:1999, 6.7.2.2p3

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

Таким образом, когда вы объявляете перечисление, вы не можете быть уверены, что априорио том, какие данные реализация C выберет для хранения этой переменной.По причинам оптимизации компилятор может не выбирать целочисленный тип на 4 байта, если вы храните константы перечисления между [-128, +127].Реализация может выбрать char для хранения перечислимой переменной, но вы не можете быть уверены.Любой целочисленный тип данных может быть выбран как время, поскольку он может хранить все возможные значения.

0 голосов
/ 30 января 2019

Константы перечислителя имеют тип int.Тип перечислителя - это неопределенный целочисленный тип, способный представлять все константы перечислителя.

6.7.2.2p4 :

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

Поскольку вы не перечислили никаких отрицательных значений, этот тип вполне может быть тип без знака .Если это так, то (E_Number)some_integer всегда будет больше или равно нулю (0==FIRST_ENUM).

Если развернуть список enum до:

typedef enum{
    NUM_NOPE=-1,
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

you 'Вынудите компилятор использовать подписанный тип, и результаты обратятся.

...