Why no switch on pointers?

Viewed 28277

For instance:

#include <stdio.h>

void why_cant_we_switch_him(void *ptr)
{
    switch (ptr) {
        case NULL:
            printf("NULL!\n");
            break;
        default:
            printf("%p!\n", ptr);
            break;
    }
}

int main(void)
{
    void *foo = "toast";
    why_cant_we_switch_him(foo);
    return 0;
}

gcc test.c -o test
test.c: In function 'why_cant_we_switch_him':
test.c:5: error: switch quantity not an integer
test.c:6: error: pointers are not permitted as case values

Just curious. Is this a technical limitation?

EDIT

People seem to think there is only one constant pointer expression. Is that is really true, though? For instance, here is a common paradigm in Objective-C (it is really only C aside from NSString, id and nil, which are merely a pointers, so it is still relevant — I just wanted to point out that there is, in fact, a common use for it, despite this being only a technical question):

#include <stdio.h>
#include <Foundation/Foundation.h>

static NSString * const kMyConstantObject = @"Foo";

void why_cant_we_switch_him(id ptr)
{
    switch (ptr) {
        case kMyConstantObject: // (Note that we are comparing pointers, not string values.)
            printf("We found him!\n");
            break;
        case nil:
            printf("He appears to be nil (or NULL, whichever you prefer).\n");
            break;
        default:
            printf("%p!\n", ptr);
            break;
    }
}

int main(void)
{
    NSString *foo = @"toast";
    why_cant_we_switch_him(foo);
    foo = kMyConstantObject;
    why_cant_we_switch_him(foo);

    return 0;
}

gcc test.c -o test -framework Foundation
test.c: In function 'why_cant_we_switch_him':
test.c:5: error: switch quantity not an integer
test.c:6: error: pointers are not permitted as case values

It appears that the reason is that switch only allows integral values (as the compiler warning said). So I suppose a better question would be to ask why this is the case? (though it is probably too late now.)

12 Answers

Because there is only one constant pointer expression

Given that only a single constant pointer expression exists, the switch statement has little to offer pointer expressions. You have cited essentially the only possible construction.

A switch compares the variable with a set of compile-time constants. Other than null, I can't see any valid compile time constants that you might compare a pointer with. For example:

switch (ptr) { 
   case &var1: printf ("Pointing to var1"); break;
   case &var2: printf ("Pointing to var2"); break;
}

var1 and var2 are likely different in each run of the program, and would not be compile time constants. One possibility might be that they are addresses of memory-mapped ports that are always fixed, but otherwise I don't see how you could easily expand this from your two cases (null / not-null).

Switch statements operate on integral values only. That's why the error message is "switch quantity not an integer." I don't think it's a technical limitation so much as it's outside the language syntax.

switch statements operate on integral expressions only. A pointer is not an integral expression.

You can explicitly convert a pointer to an integral type if you wanted to, but the proposed code is a little strange and unnatural.

So to answer your question exactly: Because there is no implicit conversion between a pointer and an integral type.

Cast ptr to an int and try again:

switch( (int)ptr )

or to be more correct:

switch( (intptr_t)ptr ) // C99 integer type to hold a pointer

You can (if you really must). Simply cast the pointer to an appropriately sized integer. For this intptr_t should be used. That is not to say I'd recommend it, but you may have your reasons.

#include <stdint.h>
#include <stdio.h>

void we_can_switch_him(void *ptr)
{
    switch ((intptr_t)ptr) {
        case (intptr_t)NULL:
            printf("NULL!\n");
            break;
        default:
            printf("%p!\n", ptr);
            break;
    }
}

int main(void)
{
    void *foo = "toast";
    we_can_switch_him(foo);
    return 0;
}

case labels expect a constant-expression, usually an integer, and pointers tend not to compare well against these except in the case of NULL. You could cast to intptr_t, but it's still nonsensical when you only have one thing you can compare against.

switch statements exist because the compiler can often turn them into a jump table, which is a concept that works best if your case labels are consecutive integers. But in the case of a pointer casted to integral type, you gain nothing over an if / else by using switch except a more cumbersome syntax.

It can be related to how switch is implemented - it seems to expect at most an integer so it can use a certain CPU register which might not be possible with a pointer.

The hitting the nail on the head answer is because switch statement need compile time things to compare to. If you could do it with run-time data, I can see boatloads of reasons to do this. In particular, say I want to figure out which function my function pointer is pointing to. The switch construction being far more elegant than a bunch of if then else if statements.

Doh! why even use a switch statement? switch statements should only be used if you have 3 or more options to choose from if you have 2 options then use an if(){} else {} statement.

joshua this is not a legitimate use of a pointer in a switch.

If you really must use a switch statement then cast it to an _int64 or long long or some integral type guaranteed to be as big as or bigger than a pointer (depends on compiler).

Also some compilers may limit the maximum size of a switch to an int or some other arbitrary size. in this case you can't use a switch statement at all.

DC

enum boolean
{
 FALSE=0,
 TRUE=!FALSE
}boolean;
...
void *ptr=NULL;
...
switch((!ptr))
{
 case FALSE:
  ...
  break;
 case TRUE:
  ...;
  break;
}
...

It's possible to switch on pointer.

You only need this change in your switch: [ptr intValue]

void why_cant_we_switch_him(void *ptr)
{
    switch ([ptr intValue]) {
        case NULL:
            printf("NULL!\n");
            break;
        default:
            printf("%p!\n", ptr);
            break;
    }
}
Related