Fun with switch case, Part I

The switch-case construction, or switch statement, provides your code with a decision tree that both easy to read and to debug. This construction is a bit daunting for the beginner, but becomes more familiar as you use it. It’s not without its quirks.

As a review, the switch statement is followed by an expression.

switch(a)

The expression is often a single variable, a above, though it can be any valid C language expression. The result of this expression is compared with a block of case statements:

case const:

Each case keyword is followed by a constant, which is compared with the switch statement’s expression. When a match is made, the statements following case are executed. Otherwise, the statements are skipped.

An optional default statement handles any condition not met with the case statements.

The switch-case construction affects flow control like if-else. It’s elegant in its construction, but not as versatile as an if statement, which can be coupled with else-if statements to hone the results.

The switch-case construction was around long before the C language was invented. Its popularity is evident in that it appears in a variety of programming languages.

One peculiarity with switch-case is that you cannot use a constant symbol in a case statement. You can use a defined constant, which is set by the precompiler. But a constant symbol isn’t really a constant in C, and some compilers don’t allow its use in switch-case.

As a reminder, you use the #define preprocessor directive to create a defined constant:

#define THREE 3

A symbolic constant is created like a variable:

const int three = 3;

It’s this second example that makes compilers choke.

2024_02_24-Lesson.c

#include <stdio.h>

int main()
{
    const int two = 2;
    int a;

    printf("1, 2, 3: ");
    scanf("%d",&a);

    switch(a)
    {
        case 1:
            puts("One!");
            break;
        case two:
            puts("Two!");
            break;
        case 3:
            puts("Three!");
            break;
        default:
            puts("You bad!");
    }

    return 0;
}

In this code, constant two is set to the value 2. It appears as the second case statement. You would assume that if two were truly a constant, it should work.

When compiled under clang, I see no warnings or errors. The code runs as expected, with two representing the value 2. But when compiled under gcc, the following is output:

2024_02_24-Lesson.c: In function ‘main’:
2024_02_2024_02_24-Lesson.c: In function ‘main’:
2024_02_24-Lesson.c:16:17: error: case label does not& reduce to an integer constant
   16 |                 case two:
      |                 ^~~~
2024_02_24-Lesson.c:5:19: warning: variable ‘two’ set but not used [-Wunused-but-set-variable]
    5 |         const int two = 2;
      |

The gcc compiler provides a better reaction than clang, which is more forgiving (at least in my configuration).

My advice is not to use a symbolic constant in a case statement. In fact, technically speaking, a constant in C isn’t a constant at all: It’s merely a symbol (variable) that the compiler accepts as “holy” or unalterable by the code. (Devious ways exist to mangle a symbolic constant, which I’m not going into here.)

While this switch-case quirk is more of a C language quirk, it’s still worth noting. For next week’s Lesson I cover another interesting aspect of the switch-case construction, one that surprised me.

2 thoughts on “Fun with switch case, Part I

  1. I find it interesting that 2024_02_24-LESSON.C should compile in Clang without any warnings—seeing that, per the language standard, case expressions should be what Cʼs grammar calls “constant expressions”.

    This should limit case expressions to literal values, preprocessor macros (“define constants”) and enum values⁽¹⁾. (Like in C#, the const keyword really should have been named readonly.)

    Would be interesting to know how Clang implements switch-case with const values. I guess it is sure to cost some performance… at least it seems to me that allowing const values would preclude the use of jump tables :-\

    Anyway, looking forward to the rest of this series… maybe even Duffʼs device will make an appearance?

    ⁽¹⁾ Admittedly, with the upcoming C23 standard constexpr should also work.

Leave a Reply