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;
}
}
精彩评论