使用if语句而不是switch case的默认值

使用if语句而不是switch case的默认值,c,if-statement,optimization,switch-statement,C,If Statement,Optimization,Switch Statement,在打开开关盒之前使用if语句并避免使用默认关键字是否正确? 例如,我想要一个程序,它把一个月的数字作为输入,告诉你它的名字。这是使用switch case语句的代码: #include <stdio.h> #include <stdlib.h> main() { int month; printf("Insert the number of the month and the program will return its name"); sca

在打开开关盒之前使用if语句并避免使用默认关键字是否正确? 例如,我想要一个程序,它把一个月的数字作为输入,告诉你它的名字。这是使用switch case语句的代码:

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

谢谢你,对不起,我的英语不是我的母语。如果我没有解释清楚,请告诉我。

为什么不正确?它是。默认设置更安全:如果您更改了案例,但未能更改if,您将陷入麻烦。

为什么不正确?它是。默认设置更安全:如果您更改案例,但未能更改if,您将陷入麻烦。

这两种方法都是有效的,除了一些细节:

定义main的方式完全过时了。它不会在严格模式下使用C99编译器编译。使用int mainvoid或int main argc,char*argv[]。 测试scanf的返回值。如果键入无法解析为数字的内容,则如果关闭输入流,scanf将返回0甚至EOF,其余代码将使用未初始化的值month。 在case子句中插入值既不有用,也不惯用。移除它们。 总是增加休息时间更安全;switch语句中最后一个子句末尾的语句,无论是否为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");
    }
}

除以下几个细节外,这两种方法均有效且等效:

定义main的方式完全过时了。它不会在严格模式下使用C99编译器编译。使用int mainvoid或int main argc,char*argv[]。 测试scanf的返回值。如果键入无法解析为数字的内容,则如果关闭输入流,scanf将返回0甚至EOF,其余代码将使用未初始化的值month。 在case子句中插入值既不有用,也不惯用。移除它们。 总是增加休息时间更安全;switch语句中最后一个子句末尾的语句,无论是否为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");
    }
}

是的,您可以使用您描述的if语句。但是,我看不出这样做有什么好处。

是的,您可以使用您描述的if语句。但是,我看不出这样做有什么好处。

原来的更好。编译器将完成几乎相同的工作,尽管由于分支预测的原因,第二个会稍微慢一点


但主要原因是代码的可维护性。添加或删除一个值时,需要使用if语句修改两个位置,而使用switch语句只修改一个位置。

原来的更好。编译器将完成几乎相同的工作,尽管由于分支预测的原因,第二个会稍微慢一点


但主要原因是代码的可维护性。添加或删除一个值时,需要使用if语句修改两个位置,而使用switch语句只修改一个位置。

我的个人视图,这可能并不完美

拥有默认案例是首选,因为它可以捕获任何未解释的案例。人们似乎依赖它,因此,gcc中有一个警告标志——Wswitch default。 因此,有时您可能会在没有默认大小写的情况下收到一些编译器警告


在您的特定情况下,使用默认情况是很好的做法

我个人的观点,可能并不完美

拥有默认案例是首选,因为它可以捕获任何未解释的案例。人们似乎依赖它,因此,gcc中有一个警告标志——Wswitch default。 因此,有时您可能会在没有默认大小写的情况下收到一些编译器警告


在您的特定情况下,使用默认情况是很好的做法

实际上,我想说的是,在使用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");
}
也就是说,回到您的初始问题,在相同的数据上混合诸如if/else和switch/case之类的流控制结构通常是不好的。这样做的原因是它不必要地复杂,所以请像您最初尝试的那样使用single switch语句,或者使用我所描述的方法
实际上,我要说的是,在使用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");
}


也就是说,回到您的初始问题,在相同的数据上混合诸如if/else和switch/case之类的流控制结构通常是不好的。原因是它不必要地复杂,所以请坚持使用您最初尝试使用的单开关语句,或者使用我上面概述的方法。

脱离主题,但您最好还是查看数组来存储这些月份。你的代码会小得多…为什么像case一样没有return 0这样的括号呢?在一天结束时,两者都完成了任务。在我看来,第一个比较干净,性能会稍好一些,因为它只需要一次比较。不过,性能不会明显提高,打印比比较要花更多的时间,所以差异不应该很明显。@UlrichEckhardt:这些是括号,返回或大小写都不需要。OP解释了为什么他认为它们很有用,但几乎没有人会这么做。离题了,但你最好还是看看存储这些月份的数组。你的代码会小得多…为什么像case一样没有return 0这样的括号呢?在一天结束时,两者都完成了任务。在我看来,第一个比较干净,性能会稍好一些,因为它只需要一次比较。不过,性能不会明显提高,打印比比较要花更多的时间,所以差异不应该很明显。@UlrichEckhardt:这些是括号,返回或大小写都不需要。OP解释了他认为它们有用的原因,但几乎没有人会这么做。您关于可维护性的评论是中肯的,但我担心您关于性能的说法是不真实的。像这样的switch语句在这两种情况下都编译成非常高效的代码。在某些编译器上,它甚至可能编译成完全相同的代码。@chqrlie:问题是,在第二个if解决方案中,可能性较小的块会排在第一位。但是处理器分支预测器通常给出较低的向前跳概率。分支预测是高度特定于处理器的。由编译器生成高效的代码。您不能通过对if分支进行排序来调整这一点。这将是过早的优化。请记住,这个switch语句最有可能编译为第1个月的单个测试,并通过12个条目的跳转表进行间接跳转。在第二段代码中,if测试可以编译成单个测试,如果编译器足够精明,它会注意到所有月份值都有一个跳转目标,因此只需要间接跳转。可维护性是这里唯一重要的关注点。@chqrlie:这是特定的,但一般来说,处理器更喜欢向后跳。这是因为循环优化。我没有说这是一件重要的事情,但我只是想指出,最后可能会有所不同。第二点可能不适用于这里-在这两种解决方案中,编译器都足够聪明,可以避免多次比较,并且可能会在这里使用简单数组。您关于可维护性的评论是中肯的,但我担心您关于性能的说法是不真实的。像这样的switch语句在这两种情况下都编译成非常高效的代码。在某些编译器上,它甚至可能编译成完全相同的代码。@chqrlie:问题是,在第二个if解决方案中,可能性较小的块会排在第一位。但是处理器分支预测器通常给出较低的向前跳概率。分支预测是高度特定于处理器的。由编译器生成高效的代码。您不能通过对if分支进行排序来调整这一点。这将是过早的优化。请记住,这个switch语句最有可能编译为第1个月的单个测试,并通过12个条目的跳转表进行间接跳转。在第二段代码中,if测试可以编译成单个测试,如果编译器足够精明,它会注意到所有月份值都有一个跳转目标,因此只需要间接跳转。可维护性是这里唯一重要的关注点。@chqrlie:这是特定的,但一般来说,处理器更喜欢向后跳。这是因为循环优化。我没有说这是一件重要的事情,但我只是想指出,最后可能会有所不同。第二点在这里可能不适用-在这两种解决方案中,编译器都足够聪明,可以避免多次比较,并且可能在这里使用简单数组。assertmonth 12&&invalid month value;:恐怕这不是你的意思。你能再详细说明一下吗?断言中的表达式参数应该是真的。你应该写assertmonth>=1&&month=1&&month啊,谢谢,相应地更新了,我的想法无效。还要注意,assert将中止程序。这在这里可能不合适,因为
OP希望打印一条特定消息,并调用系统暂停命令,使终端保持打开状态,直到用户按键。assertmonth 12&&invalid month value;:恐怕这不是你的意思。你能再详细说明一下吗?断言中的表达式参数应该是真的。你应该写assertmonth>=1&&month=1&&month啊,谢谢,相应地更新了,我的想法无效。还要注意,assert将中止程序。这在这里可能不合适,因为OP希望打印特定消息,并调用系统暂停命令以使终端保持打开状态,直到用户按键为止。括号没有错误,表达式12仍然是一个常量整数表达式,但很少在case子句中使用。最好使用惯用的风格,避免用毫无意义的不寻常的结构让读者感到惊讶。@LorenzoGramigni:如果你觉得这个答案最合适,请随意单击答案分数下面的灰色复选标记接受它。你还不能投票,因为你需要15的声望才能投票。谢谢你的回答。我把这些值括起来是因为我觉得它更正式。括号或引号仅仅是不必要的还是不正确的?它们会影响代码吗?作为一个固定的问题,我没有添加中断;也不是scanf的测试,无论如何谢谢你的建议。既然有人告诉我if方法是错误的,而你说它是等效的,甚至性能也不受影响,那么这种情况会随着更多的情况而改变吗?对于更多的情况,使用if语句不是更好吗?这样程序就不需要不必要地遍历所有的情况了?@LorenzoGramigni:程序不会遍历所有的情况,如果情况值是连续的,没有间隙或间隙很小,它可以使用恒定时间的直接分派指令,或者它可以在logN时间内使用二进制查找。编译器生成高效的代码,添加if对性能几乎没有影响,它对程序流和/或可读性非常有用。案例的数量对交换机性能几乎没有影响,而且性能对于错误案例来说并不重要。再次强调,与性能相比,更倾向于简单性、规律性、可读性。@LorenzoGramigni:如果您关心性能,请阅读本文:括号没有错误,表达式12仍然是一个常量整数表达式,但它们很少在case子句中使用。最好使用惯用的风格,避免用毫无意义的不寻常的结构让读者感到惊讶。@LorenzoGramigni:如果你觉得这个答案最合适,请随意单击答案分数下面的灰色复选标记接受它。你还不能投票,因为你需要15的声望才能投票。谢谢你的回答。我把这些值括起来是因为我觉得它更正式。括号或引号仅仅是不必要的还是不正确的?它们会影响代码吗?作为一个固定的问题,我没有添加中断;也不是scanf的测试,无论如何谢谢你的建议。既然有人告诉我if方法是错误的,而你说它是等效的,甚至性能也不受影响,那么这种情况会随着更多的情况而改变吗?对于更多的情况,使用if语句不是更好吗?这样程序就不需要不必要地遍历所有的情况了?@LorenzoGramigni:程序不会遍历所有的情况,如果情况值是连续的,没有间隙或间隙很小,它可以使用恒定时间的直接分派指令,或者它可以在logN时间内使用二进制查找。编译器生成高效的代码,添加if对性能几乎没有影响,它对程序流和/或可读性非常有用。案例的数量对交换机性能几乎没有影响,而且性能对于错误案例来说并不重要。再次强调,与性能相比,更倾向于简单性、规律性和可读性。@LorenzoGramigni:如果您关心性能,请阅读本文: