【问题标题】:How can I use ranges in a switch case statement in C?如何在 C 中的 switch case 语句中使用范围?
【发布时间】:2016-04-20 15:45:36
【问题描述】:

我的逻辑是:

if number is between 1 to 10, execute first case statement
if number is from 20 to 30, execute second case statement

除了以下解决方案之外,还有其他解决方案吗?

case '1' ... '10':
case '20' ... '30':

【问题讨论】:

  • 你唯一的选择是做你不想做的事或按照 pzaenger 的建议去做。
  • 如果你想要一个不可移植的解决方案,gcc 可以使用基于范围的案例。
  • 你的意思是1 ... 10,而不是'1' ... '10'。而且该构造是 gcc 扩展,而不是标准 C。

标签: c switch-statement


【解决方案1】:

GCC 编译器支持作为语言扩展case ranges,例如:

 switch(i) {
    case 0 ... 9: return true;
    default: return false;
 }

Clang/LLVM 也接受此语言扩展。因此,如果您有能力将代码限制为 GCC 和 Clang 编译器,请使用它。

另见this

我不知道为什么这个扩展没有包含在C11 标准中。

还要注意GCC 接受computed or indirect goto and labels as values。有案例(特别是在 生成的 C 代码),这些功能非常有用。示例可能包括一些高效的bytecode 解释器。 Ocaml virtual machine 的一些实现就是一个很好的例子。

【讨论】:

    【解决方案2】:
    void SwitchDemo(int value)
       {
       switch(value / 10)
          {
          case 0: ...; break; // 0 - 9
          case 1: ...; break; // 10 - 19
          ...
          }
       }
    

    或者,特定于问题范围:

    void SwitchDemo(int value)
       {
       switch((value-1) / 10)
          {
          case 0: ...; break; // 1 - 10
          case 1: ...; break; // 11 - 20
          ...
          }
       }
    

    【讨论】:

      【解决方案3】:

      选项 1:将case 0 用于0-9,将case 1 用于11-20 等等。

      选项 2:使用if

      选项 3:

      另一种破旧的方法是使用这样的跌倒案例:

      #include <stdio.h>
      
      int main(void) {
          int i=1;
      
          for(i=1;i<=25;i++)
          {
          switch(i)
          {
              case 1:
              case 2:
              case 3:
              case 4:
              case 5:
              case 6:
              case 7:
              case 8:
              case 9:
              case 10:
                  printf("%d  is in between 1-10\n", i);
                  break;
      
              case 11:
              case 12:
              case 13:
              case 14:
              case 15:
              case 16:
              case 17:
              case 18:
              case 19:
              case 20:
                  printf("%d  is in between 11-20\n", i);
                  break;
      
              default:
                  printf("%d  is above 20\n", i);
          }
          }
          return 0;
      }
      

      输出:

      1  is in between 1-10
      2  is in between 1-10
      3  is in between 1-10
      4  is in between 1-10
      5  is in between 1-10
      6  is in between 1-10
      7  is in between 1-10
      8  is in between 1-10
      9  is in between 1-10
      10  is in between 1-10
      11  is in between 11-20
      12  is in between 11-20
      13  is in between 11-20
      14  is in between 11-20
      15  is in between 11-20
      16  is in between 11-20
      17  is in between 11-20
      18  is in between 11-20
      19  is in between 11-20
      20  is in between 11-20
      21  is above 20
      22  is above 20
      23  is above 20
      24  is above 20
      25  is above 20
      

      https://ideone.com/Cw6HDO

      【讨论】:

      • 在这种情况下switch 可能不适合。
      • 如果您必须检查数千个数字,您应该在原始问题中提到这一点。
      • 您的意思是您有 1000 多个范围要检查?如果是,则使用if,如果数字在 1000 秒内但范围很少,则可以使用 switch
      • @user3205621 考虑编写一个小程序为您生成源代码,然后复制/粘贴或包含该文件;)
      • 注意:switch() 语句可能不支持超过 1023 个case 标签:C11 §5.2.4.1 1
      【解决方案4】:

      C 不支持除单个整数(或类似整数的东西——字符、枚举值)以外的大小写值。所以你的选择是:

      • 正如 pzaenger 在现已删除的评论中所建议的那样:将您正在使用的数字转换为您可以打开的数字(在这种情况下,除以 10)。
      • 多个 case 语句(利用 fallthrough):case 1: case 2: case 3: ... case 10: do_something();
      • 使用if 而不是case

      【讨论】:

        【解决方案5】:

        在 C 编程语言中,switch() 语句中使用的 case 语句必须指定一个值,编译器可以以某种方式将其转换为常量。 case 语句中使用的每个值在switch() 的范围内必须是唯一的。 default 关键字表示没有case 语句与switch() 语句中的表达式匹配的默认值。

        顺便说一句,请查看 Duff 的设备以展示 switch()case 的有趣用法。见How does Duff's device work?

        因此,下面显示了switch() 中正确的case 语句的几个示例:

        #define XXVAL 2
        #define CASETEST(x) (x + 5)
        
        int iValue;
        //  set the value of the variable iValue at some point
        switch (iValue) {
        case 0:
            // do the case if iValue == 0
            break;
        case XXVAL:
            // do the case if iValue == XXVAL
            break;
        case CASETEST(3):
            // do the case if iValue == CASETEST(3)
            // works because preprocessor generates the source text which is
            // then compiled and the expression can be resolved to a constant
            break;
        case CASETEST(5) * 2:
            // do the case if iValue == CASETEST(5) * 2
            // works because preprocessor generates the source text which is
            // then compiled and the expression can be resolved to a constant
            break;
        default:
            break;
        }
        

        如果您仍想将switch() 与范围case 语句一起使用,您可以提供某种机制将表达式折叠为一个或多个特定常量值。

        因此,在一个简单、琐碎的示例中,您可以执行以下操作。这是一个简单的案例,展示了最终使简单 if 语句的逻辑变得不透明的技术。这种技术对于复杂的决策和分类很有用,可以折叠成一组简单的常量。

        int foldit (int iValue)
        {
            if (iValue < 5000) return 0;
            else if (iValue < 10000) return 1;
            else if (ivalue < 20000) return 2;
            else return 9999;   // triggers the default part of the switch
        }
        
        switch (foldit(iValue)) {
        case 0:
            // do what is needed for up to but not including 5000
            break;
        case 1:
            // do what is needed for 5000 up to but not including 10000
            break;
        case 2:
            // do what is needed for 10000 up to but not including 20000
            break;
        default:
            // handle anything else
            break;
        }
        

        折叠方法可能有用的地方是当您有几个不同的结果时,可能使用过滤器来尝试对数据项进行分类。

        #define type1  0x00001
        #define type2  0x00002
        #define type3  0x00004
        #define type4  0x00008
        
        struct datatype {
            int iVal;
            int jVal; 
        };
        
        unsigned long is_a_type1(struct datatype * thing)
        {
            unsigned long retVal = 0;   // initialize to not a type1, set to type1 if turns out to be
            // do checks for the type and if so set retVal to type1 if it matches
            return retVal;
        }
        
        unsigned long is_a_type2(struct datatype * thing)
        {
            unsigned long retVal = 0;   // initialize to not a type2, set to type2 if turns out to be
            // do checks for the type and if so set retVal to type2 if it matches
            return retVal;
        }
        
        unsigned long is_a_type3(struct datatype * thing)
        {
            unsigned long retVal = 0;   // initialize to not a type3, set to type3 if turns out to be
            // do checks for the type and if so set retVal to type3 if it matches
            return retVal;
        }
        
        unsigned long is_a_type4(struct datatype * thing)
        {
            unsigned long retVal = 0;   // initialize to not a type4, set to type4 if turns out to be
            // do checks for the type and if so set retVal to type4 if it matches
            return retVal;
        }
        
        unsigned long classify (struct datatype *thing)
        {
            unsigned long ulTestResult = 0;
        
            // test to see if this is a type1 thing
            ulTestResult |= is_a_type1(thing);
        
            // test to see if this is a type2 thing
            ulTestResult |= is_a_type2(thing);
        
            // test to see if this is a type3 thing
            ulTestResult |= is_a_type3(thing);
        
            // test to see if this is a type4 thing
            ulTestResult |= is_a_type4(thing);
        
            return ulTestResult;
        }
        
        int main ()
        {
            struct datatype myThing;
            //  other source code then
            switch (classify(&myThing)) {
            case type1 | type2 | type3:
                // do stuff if this is a type1, type2, and type3 but not type4
                // that is classify() determined that myThing matched all three types.
                break;
            case type1:
                // do stuff if type1 which includes stuff you do for type2 as well under
                // special values of myThing.
                if (myThing.iVal < 50) {
                    case type2:
                        // at this point we have type2 case stuff that we do. Code above is skipped
                        // and the switch () will jump straight to here if classify() is type2.
                        //
                        // Also stuff we do if type1 and myThing.iVal < 50
                        // in other words this code is execute if classify(&myThing) is type2 or
                        // if classify(&myThink) is type1 and there is a special processing for myThing.iVal < 50
                        break;  // if classify() type2 or if classify() type1 and myThing.ival < 50
                    }
                // do stuff if only type1 and myThing.iVal >= 50
                break;
            case type2 | type3:
                // do stuff if type2 and type3 matched but none of the others.
                break;
            default:
                // any other case
                break;
            }
            return 0;
        }
        

        【讨论】:

          【解决方案6】:

          c中的switch语句只能对常量表达式进行操作,case语句不能包含动态比较。

          Example of something which is, and is not, a "Constant Expression" in C?

          对于这种简单的事情,if/else 结构可能更清晰、更简单,取决于编译器,您的 case 语句可能会被翻译成一系列分支比较语句。

          【讨论】:

          • switch()可以switch ( expression ) ...表达式部分操作一个非常量表达式
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-05
          相关资源
          最近更新 更多