Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What promoted types are used for switch-case expression comparison?

The following program prints "unknown" when compiled with different compilers. Why is that so?

#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;
}

When looking at the C++ standard (N3690 2013-05-05) I see a clause for switch:

6.4.2 The switch statement

2 The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type. Integral promotions are performed. Any statement within the switch statement can be labeled with one or more case labels as follows:

case constant-expression :  

where the constant-expression shall be a converted constant expression (5.19) of the promoted type of the switch condition. No two of the case constants in the same switch shall have the same value after conversion to the promoted type of the switch condition.

The referenced conversion clause:

4 Standard conversions

2 [ Note: expressions with a given type will be implicitly converted to other types in several contexts:
[...]
— When used in the expression of a switch statement. The destination type is integral (6.4).
[...]
—end note ]

Variable c is of type unsigned char, which is an integral type. So no promotion should be necessary!?

If the promoted type were unsigned char I would expect a comparison like c == (unsigned char)OPTION which would yield true. If the promoted type were int I would expect a comparison like (int)c == (int)OPTION) which clearly yields false.

My questions are: What is the promoted type used in the above program? What are the relevant clauses in the C and C++ standards?

like image 965
Werner Henze Avatar asked Mar 20 '23 10:03

Werner Henze


2 Answers

What types are involved?

The promoted type will be int, as described in the following section:

4.5p1 Integral promotions [conv.prom]

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.


Why does the code behave differently on different platforms?

It's implementation defined whether char is signed, or unsigned, as can be read in the following section of the Standard;

3.9.1p1 Fundamental types [basic.fundamental]

It is implementation-defined whether a char can hold negative values. Characters can be explicitly declared signed or unsigned.

...

In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.


How is that relevant?

The previous quoted section means that the cast to char on the following line doesn't have to yield the value of 253.

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

If char is made to be able to hold negative values on a platform where a char is 8bit, 253 won't fit and most likely the value of OPTION will be -3 after initialization.


In other words...

The switch, after integral promotion, in your post is semantically equivalent to the below if-else-statement, since we have one condition and a default case.

unsigned char c = 253;

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

Depending on the underlying implementation OPTION might be equal to either 253, or -3; yielding the behavior your described.


Note: All standard quotations in this post are from the final C++11 Standard (draft) n3337.

like image 110
Filip Roséen - refp Avatar answered Apr 06 '23 10:04

Filip Roséen - refp


The relevant part here is "Integral promotions are performed."

The short version of that is that types smaller than an int are promoted to an int (or an unsigned int if an int couldn't represent the full range of the values).

So you have c promoted to an int, which is 253. And you have OPTION with a value of -3 promoted to an int, which is -3. (The sign of a char is platform dependent though, so this program can behave differently on different platforms. The range of values a char can hold is platform dependent too, albeit a conversion of 253 to -3 will happen on 2s-complement platforms with 8 bit signed char, which are common.)

like image 21
nos Avatar answered Apr 06 '23 09:04

nos