1

Is it correct to use an if statement before opening a switch case and avoid using the default keyword? For example I want a program which takes as input the number of a month and tells you its name. This is the code which uses the switch case statement:

#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;
}

Then, I was wondering if I can put the non-validity condition in an if statement rather than in the default keyword. To me it seems correct since I want to verify the value before the program executes the switch case statement. What do you think, would it be correct? If I'm not asking too much, would you please tell me why?

The code with the if statement:

#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;
}

Thank you and sorry for my English but it is not my mother tongue. Let me know if I haven't explained myself clearly.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
elle
  • 13
  • 3
  • Off topic, but you'd also be better off looking at arrays to store those months. Your code would be a lot smaller... – Lee Taylor Feb 05 '16 at 19:40
  • Why are there no brackets for `return`, like `return (0)`, as there are for `case`? – Ulrich Eckhardt Feb 05 '16 at 20:16
  • At the end of the day, both get the job done. The first is cleaner in my opinion and will have slightly better performance since it will need one less comparison. The performance won't be significantly better though, printing takes far more time than comparisons so the difference shouldn't be noticeable. – Michael Goldstein Feb 05 '16 at 20:21
  • @UlrichEckhardt: these are parentheses, and none are needed for `return` or `case`. The OP explained why he thought they were useful, but almost nobody would do that. – chqrlie Feb 05 '16 at 20:22

6 Answers6

2

Why wouldn't it be correct? It is. default is just safer: if you change the cases, but fail to change the if, you'll get into trouble.

Cecilio Pardo
  • 1,717
  • 10
  • 13
2

Both approaches are valid and equivalent except for a few details:

  • The way you define main is completely obsolete. It will not compile with a C99 compiler in strict mode. Use int main(void) or int main(int argc, char *argv[]).
  • Test the return value of scanf(). If you type something that cannot be parsed as a number, scanf() will return 0 or even EOF if you close the input stream, and the rest of the code will use an uninitialized value of month.
  • It is neither useful nor idiomatic to parenthesize the value in case clauses. Remove them.
  • It is safer to always add a break; statement at the end of the last clause in a switch statement, default or not. If you ever add another clause, you wont risk omitting it.
  • Remove the default:; clause from the second code, it is useless and surprising.

The reason why the second approach might be more indicated is in case you want to do something completely different if the input is out of range, such as restarting an input operation. The if statement will allow you to separate these situations correctly, whereas just using the default clause might not be so appropriate:

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");
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • The parentheses are not incorrect, the expression `(12)` is still a constant integer expression, but they are very seldom used in `case` clauses. It is better to use *idiomatic* style and avoid surprising the reader with unusual constructs that have no purpose. – chqrlie Feb 05 '16 at 20:16
  • @LorenzoGramigni: if you feel this answer is most appropriate, feel free to click to grey checkmark below the answer score to accept it. You cannot upvote yet as you need a reputation of 15 to do that. – chqrlie Feb 05 '16 at 20:25
  • Thanks for the answer. I parenthesized the values because I thought it was more formal. Are parenthesis or quotation marks only not necessary or merely incorrect? Do they affect the code? Being a fixed problem I didn't add the `break;` nor the test for the `scanf()`, anyway thanks for the advice. Since i was told that the `if` method is wrong and you said that it's equivalent and even performance isn't affected, would that change with more `cases`? With more `cases` wouldn't it be better to use the `if` statement so that the program doesn't need to go through all the `cases` unnecessarily? – elle Feb 05 '16 at 20:55
  • @LorenzoGramigni: the program doesn't *go through all the cases*, it can use either a direct dispatch instruction in constant time if the case values are sequential with no or small gaps, or it can use a binary lookup in `log(N)` time. The compiler generates efficient code, adding the `if` has little or no impact on performance, it can be useful for the program flow and/or readability. The number of cases has little or no impact on `switch` performance, and performance wouldn't be important for error cases anyway. Again always **favor simplicity, regularity, readability** over performance. – chqrlie Feb 06 '16 at 08:30
  • @LorenzoGramigni: if you are concerned with performance, read this article: http://www.codeproject.com/Articles/100473/Something-You-May-Not-Know-About-the-Switch-Statem – chqrlie Feb 06 '16 at 09:06
0

Yes, you can use an if statement as you describe. However, I don't see any benefit from doing so.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
0

The original was better. The compiler will do pretty much the same work although the second will be a bit slower because of branch prediction.

But the main reason is maintainability of the code. When adding or removing one value you need to modify two places with the if statement while only one with the switch.

Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • Your remark about maintainability is pertinent, but I'm afraid way you say about performance is untrue. `switch` statements such as this one compile to very efficient code in both instances. On some compilers, it might even compile to exactly the same code. – chqrlie Feb 05 '16 at 19:46
  • @chqrlie : The issue is that the less probable block comes first in the second (if) solution. But the processor branch predictor usually gives lower probability to jumping forward. – Zbynek Vyskovsky - kvr000 Feb 05 '16 at 20:05
  • Branch prediction is highly processor specific. It is up to the compiler to generate efficient code. You cannot tweak this by ordering the `if` branches. This would be premature optimization. Keep in mind that this `switch` statement will most likely compile to a single test on `month-1` and an indirect jump via a 12 entry jump table. In the second code, the `if` test can be compiled into a single test and if the compiler is shrewd enough, it will notice that all `month` values have a jump target, so just the indirect jump is needed. **maintainability** is the only important concern here. – chqrlie Feb 05 '16 at 20:10
  • @chqrlie : it is specific but in general the processors prefer jumping backward. This is because of loop optimization. I didn't say that's something important here but just wanted to note that there might be a difference at the end. The second point will not apply here probably - in both solutions the compiler will be smart enough to avoid more than one comparison and probably will use simple array here. – Zbynek Vyskovsky - kvr000 Feb 05 '16 at 20:19
0

My personal view, which may not be perfect.

Having default case is preferred as it catches any unaccounted cases. People seems to rely on it, Hence, a warning flag -Wswitch-default in gcc. So, sometimes you might get some compiler warning without default case.

In your particular case, It is fine but it is good practice to use default case.

dlmeetei
  • 9,905
  • 3
  • 31
  • 38
  • 1
    `assert((month <= 0 || month > 12) && "invalid month value");`: I'm afraid this is not what you meant. – chqrlie Feb 05 '16 at 20:12
  • Could you elaborate a bit more plz – dlmeetei Feb 05 '16 at 20:14
  • The expression argument in `assert` is supposed to be true. You should write `assert(month >= 1 && month <= 12)`. It will compile to code that generates an `if` and produce an explicit error message such as `assertion failed: month >= 1 && month <= 12` if the condition is false. – chqrlie Feb 05 '16 at 20:19
  • Aah, Thanks, Updated accordingly, my invalid thinking. – dlmeetei Feb 05 '16 at 20:24
  • Also note that `assert` will abort the program. This may not be appropriate here, since the OP wants to print a specific message, and invoke the system `pause` command for the terminal to stay open until the user presses a key. – chqrlie Feb 05 '16 at 20:27
0

Actually, I would say that your code violates best practices when to use switch statements. Rather, try this:

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");
}

That said, getting back to your ininital question, mixing flow control structures like if/else with switch/case on the same data is usually bad. The reason for that is that it is unnecessarily complicated, so stick with either the single switch statement like in your initial attempt or use the approach I sketched above.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55