Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-traditional uses of switch statement

Recently I found out that the body of a switch can be any statement (C99 6.8.4). The idea was first suggested to me by this: https://stackoverflow.com/a/9220598/515212

So one can have switch statements like

void f(int n)
{
    switch (n)
    case 0:
        printf("zero\n");
}

or even put ifs, whiles, etc.

void f(int n)
{
    switch (n) 
    if (1)
    {
        case 0:
            printf("zero\n");
    }
    else
        while (--n)
        {
            default:
                printf("non-zero\n");
        }
}

Just out of interest, I was wondering whether this syntax has some usage or is just an artifact of how the switch statement is defined in the standard?

like image 758
martinkunev Avatar asked Dec 06 '25 17:12

martinkunev


2 Answers

You can consider the switch statement as a code block with labels (case(s) are indeed labels) where the control is passed with a goto statement.

Something like

void f(int n)
{
    if ( n == 0 ) goto Label_case_0;
    else goto Label_default;

    {
        if ( 1 )
        {
            Label_case_0:
            printf("zero\n");
        }
        else 
            while (--n)
            {
                Label_default:
                printf("non-zero\n");
            }
    }
}

In my opinion it is not a good idea to place case labels inside some other control structures because this makes the code difficult to read and can lead to errors.

like image 175
Vlad from Moscow Avatar answered Dec 08 '25 10:12

Vlad from Moscow


This is valid C code. It origins from assembly where every conditional statement uses if, goto and label jumps.

Implementation of array copy using this feature is called Duff's Device

void copy(char *from, char *to, int count)
{
    int n = (count + 7) / 8;

    switch(count % 8) {
        case 0: do { *to++ = *from++;
        case 7:      *to++ = *from++;
        case 6:      *to++ = *from++;
        case 5:      *to++ = *from++;
        case 4:      *to++ = *from++;
        case 3:      *to++ = *from++;
        case 2:      *to++ = *from++;
        case 1:      *to++ = *from++;
                   } while(--n > 0);
    }
}

When you replace while with if and goto

void copy(char *from, char *to, int count)
{
    int n = (count + 7) / 8;

    switch(count % 8) {
        case 0: 
        loop:        *to++ = *from++;
        case 7:      *to++ = *from++;
        case 6:      *to++ = *from++;
        case 5:      *to++ = *from++;
        case 4:      *to++ = *from++;
        case 3:      *to++ = *from++;
        case 2:      *to++ = *from++;
        case 1:      *to++ = *from++;
        if(--n > 0) goto loop;
    }
}

Then replace switch with if and goto

void copy(char *from, char *to, int count)
{
    int n = (count + 7) / 8;

    if(count%8==7)goto case7;
    if(count%8==6)goto case6;
    if(count%8==5)goto case5;
    if(count%8==4)goto case4;
    if(count%8==3)goto case3;
    if(count%8==2)goto case2;
    if(count%8==1)goto case1;
    if(count%8==0)goto case0; // this can be omitted

    case0:                    // this can be omitted
    loop:        *to++ = *from++;
    case7:       *to++ = *from++;
    case6:       *to++ = *from++;
    case5:       *to++ = *from++;
    case4:       *to++ = *from++;
    case3:       *to++ = *from++;
    case2:       *to++ = *from++;
    case1:       *to++ = *from++;

    if(--n > 0) goto loop;
}

Which is functionally (almost) equivalent to

void copy(char *from, char *to, int count)
{
    while(--n > 0) { 
        *to++ = *from++;
    }
}

It's almost equivalent because in the last implementation loop check is performed 8 times more often, what have impact on performance.

like image 31
mleko Avatar answered Dec 08 '25 10:12

mleko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!