Почему нет переключения на указатели? - PullRequest
25 голосов
/ 22 февраля 2010

Например:

#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

Просто любопытно. Это техническое ограничение?

EDIT

Люди, кажется, думают, что есть только одно константное выражение указателя. Правда ли это правда? Например, здесь есть общая парадигма в Objective-C (это на самом деле только C, кроме NSString, id и nil, которые являются просто указателями, поэтому это все еще актуально - я просто хотел отметить что является , на самом деле это обычное использование, несмотря на то, что это всего лишь технический вопрос):

#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

Похоже, причина в том, что switch допускает только целые значения (как сказано в предупреждении компилятора). Поэтому я полагаю, что лучше задать вопрос, почему это так? (хотя, возможно, сейчас уже слишком поздно.)

Ответы [ 11 ]

10 голосов
/ 22 февраля 2010

Поскольку существует только одно константное выражение указателя

Учитывая, что существует только одно константное выражение указателя, оператор switch мало что может предложить для выражений указателя. Вы указали, по сути, единственно возможную конструкцию.

8 голосов
/ 22 февраля 2010

Переключатель сравнивает переменную с набором констант времени компиляции . Кроме нуля, я не вижу никаких допустимых констант времени компиляции, с которыми вы могли бы сравнить указатель. Например:

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

var1 и var2, вероятно, различаются при каждом запуске программы и не будут постоянными времени компиляции. Возможно, одна из них заключается в том, что это адреса портов, отображаемых в память, которые всегда фиксированы, но в противном случае я не понимаю, как вы могли бы легко расширить это из ваших двух случаев (ноль / не-ноль).

8 голосов
/ 22 февраля 2010

switch операторы работают только с интегральными выражениями. Указатель не является интегральным выражением.

Вы можете явно преобразовать указатель в целочисленный тип, если хотите, но предлагаемый код немного странный и неестественный.

Итак, чтобы точно ответить на ваш вопрос: потому что нет неявного преобразования между указателем и целочисленным типом.

8 голосов
/ 22 февраля 2010

Операторы переключения работают только на целые значения. Вот почему сообщение об ошибке «количество переключений, а не целое число». Я не думаю, что это техническое ограничение, поскольку оно находится за пределами синтаксиса языка.

7 голосов
/ 22 февраля 2010

Приведите ptr к int и попробуйте снова:

switch( (int)ptr )

или, если быть более точным:

switch( (intptr_t)ptr ) // C99 integer type to hold a pointer
4 голосов
/ 22 февраля 2010

Вы можете (если вы действительно должны). Просто приведите указатель к соответствующему размеру целому числу. Для этого следует использовать intptr_t. Это не значит, что я бы рекомендовал это, но у вас могут быть свои причины.

#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;
}
2 голосов
/ 22 февраля 2010

case метки ожидают константное выражение, обычно целое число, а указатели, как правило, плохо сравниваются с ними, за исключением случая NULL. Вы можете привести к intptr_t, но это все еще бессмысленно, когда у вас есть только одна вещь, с которой вы можете сравнить.

Существуют операторы

switch, потому что компилятор часто может превращать их в таблицу переходов , которая лучше всего работает, если ваши метки case являются последовательными целыми числами. Но в случае указателя, приведенного к целочисленному типу, вы не получите ничего по сравнению с if / else, используя переключатель, кроме более громоздкого синтаксиса.

1 голос
/ 22 февраля 2010

Это может быть связано с тем, как реализован коммутатор - кажется, он ожидает максимум целое число, поэтому он может использовать определенный регистр ЦП, который может быть невозможен с указателем.

0 голосов
/ 17 апреля 2012
enum boolean
{
 FALSE=0,
 TRUE=!FALSE
}boolean;
...
void *ptr=NULL;
...
switch((!ptr))
{
 case FALSE:
  ...
  break;
 case TRUE:
  ...;
  break;
}
...

Возможно включить указатель.

0 голосов
/ 07 февраля 2011

Вам нужно только это изменение в вашем переключателе: [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;
    }
}
...