Подтвердить что ты не робот

Есть ли полезный случай с использованием оператора switch без брекетов?

В H & S5 я столкнулся с "самым причудливым" оператором switch (8.7.1, стр. 277), не использующим фигурные скобки. Здесь образец:

switch (x)
    default:
    if (prime(x))
        case 2: case 3: case 5: case 7:
            process_prime(x);
    else
        case 4: case 6: case 8: case 9: case 10:
            process_composite(x);

Идея заключается в том, чтобы избежать накладных расходов prime(x) для наиболее распространенных небольших чисел.

Когда я увидел это выражение, я был смущен отсутствующими фигурками, но, проверяя официальную грамматику (C1X pre-standard, 6.8.4, стр. 147), синтаксис был верным: оператор switch просто имеет оператор после выражения switch и закрывающей круглой скобки.

Но в моей практике программирования я никогда не встречал такого любопытного оператора switch (и я не хотел бы видеть в коде, за который я должен взять на себя ответственность), но я начал задаваться вопросом:

Кто-нибудь из вас знает такое выражение switch, одно без использования фигурных скобок, но все еще имеющее смысл? Не только switch (i); (что является законным, но и NOP), но используя по крайней мере две метки меток, имеющие какую-то полезную цель?

4b9b3361

Ответ 1

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

#define DEBUG_PRINT(...) switch (!debug_mode) case 0: fprintf(__VA_ARGS__)

С этим у вас нет сюрпризов, если пользователь этого макроса помещает это в дополнительное условие

if (unclear) DEBUG_PRINT(stderr, "This is really %unclear\n", unclear);
else {
 // do something reasonable here
}

Такой макрос отладки имеет то преимущество, что он всегда компилируется (а затем в конечном итоге оптимизирован). Таким образом, код отладки должен оставаться в силе через все время работы программы.

Также обратите внимание на то, что важно, чтобы switch не использовал {}, иначе пример if/else тоже не работал. Все это может быть достигнуто с помощью других средств (if/else, (void)0 и do/while трюков), но это один из самых удобных я знаю.

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

Ответ 2

Вот пример, написанный Деннисом Ритчи в 1972 году во время его работы над первым компилятором C. Модуль c02.c, связанный у подножия страницы, с которой я только что связался, включает

easystmt()
{
    extern peeksym, peekc, cval;

    if((peeksym=symbol())==20)  /* name */
        return(peekc!=':');  /* not label */
    if (peeksym==19) {      /* keyword */
        switch(cval)
        case 10:    /* goto */
        case 11:    /* return */
        case 17:    /* break */
        case 18:    /* continue */
            return(1);
        return(0);
    }
    return(peeksym!=2);     /* { */
}

Из-за того, что он прочитал свой код 1972 года, он понял, что Деннис был поклонником операторов switch - он использовал их совсем немного. Это не удивительно, учитывая, что почти все было закодировано как int частично из-за отсутствия других возможностей типа данных. Его реализация компилятора не использовала никаких структур на этом этапе, потому что он был только в середине добавления их к языку. Динамическая отправка, vtables и полиморфизм были далеко. Я пробовал и не смог найти ссылку для этого, но если я правильно напомню, что Деннис "изобрел" заявления о переключении или, по крайней мере, представил идеи, ведущие к форме, которую они принимают на C, и считал их одним из своих лучших или гордых дополнений к языку.

Возможность оставить фигурные скобки делает заявления операторов формально похожими на выражения if, for, do и while, помогая упростить и унифицировать грамматику. См. Инструкции выбора и операции итерации в грамматике C (например, в приложении A13 Кернигана и Ричи, стр. 236-237 в моей копии), где эти вещи определены.

Очевидно, что всегда можно добавить фигурные скобки, но, возможно, это кажется тяжелым для таких простых примеров, как этот. Этот пример может быть закодирован как дизъюнктивный оператор if, но я думаю, что одна из идей, которые Деннис использовал для переключения, заключалась в том, что компилятору более ясно предлагается возможность оптимизировать реализацию ветвящейся логики на основе конкретных констант.

Ответ 3

Я подумал о другом случае.

Предположим, что у меня есть счетчик типа unsigned char, указывающий количество итераций цикла, но если счетчик равен нулю, ему нужно пройти цикл 256 раз. Если мое мышление верное, вы можете закодировать его следующим образом:

uint8_t counter;
/* counter will get its value here somewhere */
switch (counter)
    default:
        while (0 < counter)
        {
            case 0:
                /* Perform action */
                counter--;
        }

Это, конечно, предполагает, что underflow из 0x00 приводит к 0xFF для unsigned char. Но это работает для всех моих сред, хотя PC Lint будет жаловаться... И да, он содержит фигурные скобки, но только для while, а не для switch. Если вы знаете что-то получше, позвольте мне это услышать!

Я бы запрограммировал это? Никогда!... ну, на маленьком 8-битном процессоре я даже мог бы!: -)

Ответ 4

На практике коммутаторы используются с брекетами (даже в устройстве Duff) для удобства чтения. И добавление скобок не вредит.

Ответ 5

Раздел 6.8.4.2 В заявлении коммутатора говорится:

Оператор switch приводит к тому, что управление переходит в, в или из оператор, который является корпусом коммутатора, в зависимости от значения контролирующее выражение, а также наличие метки по умолчанию и значения любых меток на корпусе или в корпусе переключателя. Случай или дефолт ярлык доступен только в ближайшем закрывающем переключателе утверждение.

Термины switch-body и ближайший закрывающий оператор switch, похоже, не требуют скобок. Итак, вы правы, это выглядит странно, но законно. (Никогда не видел этого раньше)