2016-02-05 7 views
1

Правильно ли использовать инструкцию if перед открытием switchcase и избежать использования ключевого слова default? Например, мне нужна программа, которая принимает в качестве входного числа месяц и сообщает свое имя. Это код, который использует оператор switchcase:Использование инструкции if вместо значения по умолчанию для корпуса коммутатора

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

main() { 
    int month; 
    printf("Insert the number of the month and the program will return its name"); 
    scanf("%i", &month); 
    switch (month) { 
     case (1): 
     printf("The month is January"); 
     break; 
     case (2): 
     printf("The month is February"); 
     break; 
     case (3): 
     printf("The month is March"); 
     break; 
     case (4): 
     printf("The month is April"); 
     break; 
     case (5): 
     printf("The month is May"); 
     break; 
     case (6): 
     printf("The month is June"); 
     break; 
     case (7): 
     printf("The month is July"); 
     break; 
     case (8): 
     printf("The month is August"); 
     break; 
     case (9): 
     printf("The month is September"); 
     break; 
     case (10): 
     printf("The month is October"); 
     break; 
     case (11): 
     printf("The month is November"); 
     break; 
     case (12): 
     printf("The month is December"); 
     break; 
     default: 
     printf("not valid"); 
    } 
    system("pause"); 
    return 0; 
} 

Тогда мне было интересно, если я могу поставить условие недействительности в if заявления, а не в default ключевом слове. Мне кажется правильным, поскольку я хочу проверить значение до того, как программа выполнит оператор switchcase. Как вы думаете, было бы правильно? Если я не прошу слишком много, не могли бы вы рассказать мне, почему?

Код с if высказыванием:

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

main() { 
    int month; 
    printf("Insert the number of the month and the program will return its name"); 
    scanf("%i", &month); 
    if (month >= 1 && month <= 12) { 
     switch (month) { 
      case (1): 
      printf("The month is January"); 
      break; 
      case (2): 
      printf("The month is February"); 
      break; 
      case (3): 
      printf("The month is March"); 
      break; 
      case (4): 
      printf("The month is April"); 
      break; 
      case (5): 
      printf("The month is May"); 
      break; 
      case (6): 
      printf("The month is June"); 
      break; 
      case (7): 
      printf("The month is July"); 
      break; 
      case (8): 
      printf("The month is August"); 
      break; 
      case (9): 
      printf("The month is September"); 
      break; 
      case (10): 
      printf("The month is October"); 
      break; 
      case (11): 
      printf("The month is November"); 
      break; 
      case (12): 
      printf("The month is December"); 
      break; 
      default:; 
     } 
    } else { 
     printf("not valid"); 
    } 
    system("pause"); 
    return 0; 
} 

Спасибо и извините за мой английский, но это не мой родной язык. Дайте мне знать, если я не объяснил себя четко.

+0

Отключить тему, но вам также будет лучше смотреть на массивы, чтобы хранить эти месяцы. Ваш код будет намного меньше ... –

+0

Почему нет скобок для 'return', например' return (0) ', как есть для' case'? –

+0

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

ответ

2

Оба подхода являются действительными и эквивалентны за исключением некоторых деталей:

  • способ определения main полностью устарели. Он не будет компилироваться с компилятором C99 в строгом режиме. Используйте int main(void) или int main(int argc, char *argv[]).
  • Проверьте возвращаемое значение scanf(). Если вы набираете то, что не может быть проанализировано как число, scanf() вернет 0 или даже EOF, если вы закроете входной поток, а остальная часть кода будет использовать неинициализированное значение month.
  • Не полезно и не идиоматично вставлять в скобки значение в статьях case. Удалить их.
  • Безопаснее всегда добавлять оператор break; в конце последнего предложения в инструкции switch, default или нет. Если вы когда-нибудь добавите другое предложение, вы не рискнете его опустить.
  • Извлеките пункт default:; из второго кода, это бесполезно и удивительно.

Причина, по которой второй подход может быть более указан, заключается в том, что вы хотите сделать что-то совершенно другое, если вход за пределами допустимого диапазона, например перезапуск операции ввода. if заявление позволит правильно разделить эти ситуации, в то время как только с помощью предложения default может быть не так уместно:

for (;;) { 
    int n, month; 
    printf("Enter a number between 1 and 12: "); 
    n = scanf("%d", &month); 
    if (n == EOF) { 
     printf("Unexpected end of file\n"); 
     exit(1); 
    } 
    if (n != 1) { 
     printf("Invalid input\n"); 
     scanf("%*[^\n]%*c"); /* flush the pending input */ 
     continue; 
    } 
    if (month >= 1 && month <= 12) { 
     switch (month) { 
      case 1: 
      printf("The month is January\n"); 
      break; 
      ... 
      case 12: 
      printf("The month is December\n"); 
      break; 
     } 
     handle_month(month); // perform other tasks 
     break; 
    } else { 
     printf("Invalid month number\n"); 
    } 
} 
+0

Скобки не являются некорректными, выражение '(12)' все еще является константным целочисленным выражением, но они очень редко используются в предложениях case. Лучше использовать * идиоматический стиль и не удивлять читателя необычными конструкциями, которые не имеют никакой цели. – chqrlie

+0

@LorenzoGramigni: если вы считаете, что этот ответ наиболее уместен, не стесняйтесь нажимать на серое галочку под номером ответа, чтобы принять его. Вы не можете повышать рейтинг, так как вам нужна репутация 15, чтобы сделать это. – chqrlie

+0

Спасибо за ответ. Я ввел в скобки значения, потому что я думал, что это более формально. Скобки или кавычки не нужны или просто неверны? Они влияют на код? Будучи фиксированной проблемой, я не добавлял 'break;' и тест для 'scanf()', в любом случае спасибо за совет. Поскольку мне сказали, что метод 'if' ошибочен, и вы сказали, что он эквивалентен, и даже производительность не влияет, это изменится с более« случаями »? С большим количеством «случаев» было бы лучше использовать оператор 'if', чтобы программа не нуждалась во всех случаях« без необходимости »? –

2

Почему бы не быть верным? Это. default просто безопаснее: если вы меняете cases, но не можете изменить if, вы попадете в беду.

0

Да, вы можете использовать if заявление, как вы описываете. Однако я не вижу никакой пользы от этого.

0

Оригинал был лучше. Компилятор выполнит почти такую ​​же работу, хотя второй будет немного медленнее из-за предсказания ветвления.

Но основная причина заключается в ремонтопригодности кода. При добавлении или удалении одного значения вам нужно изменить два места с помощью оператора if, а только с switch.

+0

Ваше замечание об ремонтопригодности уместно, но я боюсь, что вы говорите, что производительность невыполнима. Операторы 'switch', такие как этот, компилируются в очень эффективный код в обоих экземплярах. В некоторых компиляторах он может даже скомпилировать точно такой же код. – chqrlie

+0

@chqrlie: проблема в том, что менее вероятный блок приходит первым во втором (если) решении. Но предиктор ветви процессора обычно дает меньшую вероятность перепрыгнуть вперед. –

+0

Прогнозирование ветвлений имеет специфический процессор. Компилятор должен генерировать эффективный код. Вы не можете настроить это, заказывая ветви 'if'. Это будет преждевременная оптимизация. Имейте в виду, что этот оператор 'switch', скорее всего, скомпилируется с одним тестом на« месяц-1 »и косвенным прыжком через таблицу перехода из 12 элементов. Во втором коде тест 'if' может быть скомпилирован в один тест, и если компилятор достаточно проницателен, он заметит, что все значения« месяца »имеют цель перехода, поэтому необходим косвенный переход. ** Ремонтопригодность ** является исключительно важной проблемой здесь. – chqrlie

0

Мой личный взгляд, который может быть не идеальным.

Имея default случай предпочтительнее, поскольку он ловит неучтенные случаи. Люди, похоже, полагаются на него, следовательно, предупреждающий флаг -Wswitch-default в gcc. Итак, иногда вы можете получить предупреждение о компиляторе без default.

В вашем конкретном случае, это нормально, но good practice использовать default.

+1

'assert ((month <= 0 || month> 12) &&" недопустимое значение месяца ");': Я боюсь, что это не то, что вы имели в виду. – chqrlie

+0

Не могли бы вы рассказать немного больше plz – dlmeetei

+0

Аргумент выражения в 'assert' должен быть истинным. Вы должны написать 'assert (month> = 1 && month <= 12)'. Он скомпилирует код, который генерирует 'if' и создает явное сообщение об ошибке, такое как' assertion failed: month> = 1 && month <= 12', если условие ложно. – chqrlie

0

Фактически, я бы сказал, что ваш код нарушает лучшие практики при использовании операторов switch. Скорее всего, попробуйте следующее:

if (month >= 1 && month <= 12) { 
    char const*const names[] = { 
     "January", 
     "February", 
     ... 
     "November", 
     "December", 
    }; 
    printf("The month is %s\n", names[month -1]); 
} else { 
    printf("not valid\n"); 
} 

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