Какие продвигаемые типы используются для сравнения выражений с переключателями?

Следующая программа выводит "unknown" при компиляции с разными компиляторами. Почему это так?

#include "stdio.h"

const char OPTION = (char)(unsigned char)253;

int main(int argc, char* argv[])
{
    unsigned char c = 253;
    switch (c)
    {
    case OPTION:
        printf("option\n");
        break;
    default:
        printf("unknown\n");
        break;
    }

    return 0;
}

При взгляде на стандарт C++ (N3690 2013-05-05) я вижу пункт для switch:

6.4.2 Оператор switch

2 Условие должно быть целого типа, типа перечисления или типа класса. Если это тип класса, условие контекстуально неявно преобразуется (раздел 4) в тип целого или перечисления. Интегральные акции выполняются. Любой оператор внутри оператора switch может быть помечен одной или несколькими метками регистра следующим образом:

case constant-expression :  

где константное выражение должно быть преобразованным константным выражением (5.19) повышенного типа условия переключения. Никакие две константы регистра в одном и том же коммутаторе не должны иметь одинаковое значение после преобразования в продвинутый тип условия коммутатора.

Ссылочное условие преобразования:

4 Стандартные преобразования

2 [Примечание: выражения с данным типом будут неявно преобразованы в другие типы в нескольких контекстах:
[...]
- При использовании в выражении оператора switch. Тип назначения является целым (6.4).
[...]
—Конечная записка]

Переменная c имеет тип unsigned char, который является целым типом. Так что продвижение не должно быть необходимым!?

Если рекламируемый тип был unsigned char Я ожидал бы сравнение как c == (unsigned char)OPTION который дал бы истину. Если рекламируемый тип был int Я ожидал бы сравнение как (int)c == (int)OPTION) что явно дает ложь.

Мои вопросы: какой продвигаемый тип используется в вышеуказанной программе? Каковы соответствующие положения в стандартах C и C++?

3 ответа

Решение

Какие типы участвуют?

Повышенный тип будет int, как описано в следующем разделе:

4.5p1 Интегральные акции [conv.prom]

Значение типа integer, отличного от bool,char16_t,char32_t, или же wchar_t чей ранг целочисленного преобразования (4.13) меньше, чем ранг int может быть преобразовано в тип значения int если int может представлять все значения типа источника; в противном случае исходное значение может быть преобразовано в значение типа unsigned int,


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

Его реализация определяется, является ли тип char подписанным или неподписанным, что можно прочитать в следующем разделе Стандарта;

3.9.1p1 Основные типы [basic.fundamental]

Это определяется реализацией, является ли char может содержать отрицательные значения. Символы могут быть явно объявлены signed или же unsigned,

...

В любой конкретной реализации, простой char Объект может принимать либо те же значения, что и signed char или unsigned char; какой из них определяется реализацией.


Насколько это актуально?

Предыдущий цитируемый раздел означает, что приведение к char на следующей строке не нужно давать значение 253,

const char OPTION = (char)(unsigned char)253;

Если символ сделан так, чтобы иметь возможность хранить отрицательные значения на платформе, где символ 8 бит, 253 не подходит и, скорее всего, значение OPTION будет -3 после инициализации.


Другими словами...

Переключение после встроенного продвижения в вашем сообщении семантически эквивалентно приведенному ниже условному выражению if-else, поскольку у нас есть одно условие и случай по умолчанию.

unsigned char c = 253;

//   .---------.-------------------- integral promotion
//   v         v
if ((int)c == (int)OPTION) {
  printf ("OPTION\n");
} else {
  printf ("DEFAULT\n");
}

В зависимости от базовой реализации OPTION может быть равен либо 253, или же -3; уступая поведению твоему описанному.


Примечание. Все стандартные цитаты в этом посте взяты из окончательного стандарта C++11 (черновик) n3337.

Соответствующая часть здесь "Интегральные рекламные акции выполняются".

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

Так что у тебя есть c повышен до Int, который составляет 253. И у вас есть OPTION со значением -3, повышенным до целого, которое равно -3. (Знак char Это зависит от платформы, поэтому эта программа может вести себя по-разному на разных платформах. Диапазон значений, которые может содержать символ, также зависит от платформы, хотя преобразование 253 в -3 произойдет на платформах с комплементом 2s с 8-битным символом со знаком, что является обычным.)

Как сказано в приведенной вами цитате "Интегральные рекламные акции выполняются". Так что в этом выражении

switch (c)

c будет преобразован в тип int и будет иметь значение 253, поскольку c является целым объектом без знака.

В этом ярлыке

case OPTION:

так как OPTION является символом со знаком (я полагаю, что по умолчанию char ведет себя как символ со знаком), то бит знака будет распространяться.

Таким образом, контроль будет передан на этикетку default потому что ( int) ( unsigned char) 253 не равно (int) (подписанному char) 253.

Другие вопросы по тегам