2017-02-06 4 views
8

я натыкался на код, который выглядит следующим образом:Почему аргумент case разрешен внутри блока для другого случая?

switch(i) { 
    case 2: { 
     std::cout << "2"; 
     break; 
    case 3: 
     std::cout << "3"; 
     break; 
    } 
    case 4: { 
     std::cout << "4"; 
     break; 
    }  
    } 

Обратите внимание, что case 2 открывает блок с фигурной скобкой, которая закрывается только после case 3. Сначала это выглядело как опечатка, которая может вызвать ошибку компилятора или, что еще хуже, игнорировать case 3. Но он отлично работает в C++ и выводит 3, если я 3. Я пришел из java-фона, поэтому мое понимание логических блоков в C++ может отсутствовать. Поэтому мой вопрос: это намеренное поведение?

+1

Внутренние брекеты скромны, но не навреди. Есть хуже, чем вы можете делать с ярлыками case. –

+7

Операторы 'switch' в C и C++ не такие _logic_, как вы думаете. См. [Устройство Даффа] (https://en.wikipedia.org/wiki/Duff%27s_device) для крайнего примера. – rodrigo

+0

Случаи охвачены переключателем - ничего другого. Одна из многих причин не использовать их. –

ответ

5

switch заявление в C/C++ является прославленным заявлением goto (с некоторыми преимуществами оптимизации).

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

9

Вы можете, но не должны, злоупотреблять ярлыками наклейки в switch намного хуже, чем это - и намного хуже, чем Duff's Device. У устройства Даффа есть сомнительная привилегия быть практически правдоподобной и все же может рассматриваться как злоупотребление switch.

Не все злоупотребления switch могут претендовать на правдоподобность. Например, это составляет, как C или C++, даже установить строгие предупреждения:

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 

int main(int argc, char **argv) 
{ 
    unsigned seed; 
    if (argc == 2) 
     seed = atoi(argv[1]); 
    else 
     seed = time(0); 
    printf("seed: %u\n", seed); 
    srand(seed); 

    int i = rand() % 10; 
    int j = 21; 
    int k = 37; 
    printf("i: %d\n", i); 
    switch (i) 
    { 
    case 1: 
     for (j = 10; j > i; j--) 
     { 
     case 2: 
      printf("case 2:\n"); 
      for (k = j - 1; k > 0; k--) 
      { 
      case 6: 
       printf("case 6:\n"); 
      default: 
       printf("%d-%d-%d\n", i, j, k); 
      } 
     case 5: 
      printf("case 5:\n"); 
      printf("%d-%d\n", i, j); 
      break; 
     } 
     break; 
    case 3: 
     printf("case 3:\n"); 
     break; 
    } 
    return 0; 
} 

Обработка аргумент позволяет установить семя, так что вы можете воспроизвести результаты, если вы хотите. Я не буду утверждать, что это полезно; действительно, это не полезно. Обратите внимание: break внутри петель разрывает петлю, а не switch.

В основном, case этикетки (и default) должны быть в пределах сферы действия switch, и связаны с самым внутренним ограждающих switch. На них есть и другие ограничения. Вы должны быть осторожны, чтобы не перепрыгивать через переменные инициализации и т. Д. (Поэтому j и k определены за пределами switch()). Но в остальном они просто ярлыки, и контроль будет течь к ним, когда «подходит».