开发者

Why no switch on pointers?

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 expr开发者_StackOverflowession. 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.)


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜