【问题标题】:switch case: error: case label does not reduce to an integer constant切换案例:错误:案例标签不会减少为整数常量
【发布时间】:2012-12-28 12:41:41
【问题描述】:
int value;

const int signalmin = some_function();

switch(value)
{
   case signalmin:
   break;
}

我读取了 some_function 的值并使用该 int 值来打开 switch case。 C99 编译器回馈:

错误:case 标签没有减少为整数常量

但我不能使用#define,因为在切换执行之前正在读取 int 值。

【问题讨论】:

    标签: c


    【解决方案1】:

    switch 标签必须是常量表达式,它们必须在编译时进行评估。如果要在运行时值上进行分支,则必须使用 if

    const-qualified 变量不是常量表达式,它只是一个不允许修改的值。

    整数常量表达式的形式详见6.6(6)【C99和C2011标准的n1570草案】:

    6 整数常量表达式应该是整数类型并且应该只有操作数 即整数常量、枚举常量、字符常量、sizeof 结果是整数常量、_Alignof 表达式和浮点数的表达式 作为强制转换的直接操作数的常量。在整数常量中转换运算符 表达式只能将算术类型转换为整数类型,除非作为 sizeof_Alignof 运算符的操作数。

    仅允许结果为整数常量的sizeof 表达式的限制排除了操作数为可变长度数组的sizeof 表达式。

    【讨论】:

    • 好吧,const 不是整数常量表达式,但是 static const 呢?
    • @Cyan 不在 C 语言中(我上次查看)。在其他语言中,情况可能会有所不同。
    • 值得注意的是,在 C++ 中,const int foo = some_expr; 使 foo 成为一个常量表达式当且仅当 some_expr 是一个常量表达式。 (我不能 100% 确定我是否完全正确地说明了规则。)在 C 中,const 仅表示只读。
    【解决方案2】:

    让我举个例子。以下是在 gcc 版本 4.6.3 上测试的,并设置了标志 -std=c99 -pedantic

    #define SOME_HARDCODED_CONSTANT 0 //good
    int foo(int i, int b){
        const int c=0; //bad
        int a=0; //bad
    
        switch(i){
            case c:     //compile error
            case a:     //compile error.
            case (b+a): //compile error
            case SOME_HARDCODED_CONSTANT: //all good
            case 5: //all good
        }
    }
    

    正如其他人所指出的,case 参数无法在运行时进行评估。使用 if-else 块来执行此操作。

    【讨论】:

    • 显然是最佳答案。谢谢。
    • 有趣的案例(不是双关语):case (1 + 3): 这一切都很好(gcc 9 -Wall -Wextra -Werror -pedantic
    【解决方案3】:

    在 C 中,所有case 标签必须是编译时间 常量。在 C 中,const 限定符不会创建编译时常量,它只是指定运行时变量是只读的。

    switch 不是您尝试执行的适当控制结构。

    【讨论】:

      【解决方案4】:

      在 C 中,变量不能在 switch case 标签中使用,而只能在其中使用常量表达式。

      【讨论】:

        【解决方案5】:

        在 OSX 上,clang 似乎毫无怨言地将常量作为大小写标签。

        #include <stdio.h>
        
        #define SOME_HARDCODED_CONSTANT 0 //good for sure
        int foo(int i, int b){ 
            const int c=1; //no problem!!!
        
            switch(i){
                case SOME_HARDCODED_CONSTANT: //all good
                    printf("case SOME_HARDCODED_CONSTANT\n"); break;
                case c:     //no compile error for clang
                    printf("case c\n"); break;
                case 5: //all good
                    printf("case 5\n"); break;
            }   
            return i+b;
        }
        
        int main() {
            printf("test foo(1,3): %d\n", foo(1,3));
        }
        

        输出:

        $> cc test.c -o test; ./test 
        case c
        test foo(1,3): 4
        

        【讨论】:

        • 如果你问它,它确实会抱怨:caseconst.c:10:14: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]。这里,const int c = 1; 让编译器在编译时知道值(作为整数常量表达式),因此它可以将 c 视为switch 中的整数常量表达式。如果您使用函数调用初始化 c,预计会出现编译错误。
        • 您可以使用不同的版本。我的 cc 没有任何抱怨并报告版本如下。 $> cc --version $> Apple LLVM 版本 6.0 (clang-600.0.57) (基于 LLVM 3.5svn) $> 目标:x86_64-apple-darwin14.3.0
        • 是的,不同的版本。然而,当你要求它挑剔时,它必须警告,-std=c11 -Weverything 必须告诉你,-Wgnu-folding-constant-pedantic-errors 必须导致编译错误。但既然它是一个相当无害的东西,你需要在它提到它之前告诉编译器真的很挑剔。
        • 挑剔的东西并不总是必要的。常量折叠/传播对于一个体面的编译器来说是非常重要的,它可以确定 case 标签是否为常量,而不管声明如何。在这种情况下,除非用户要求,否则在显式常量声明的情况下,默认情况下显然不需要警告。
        • 是的,这就是为什么编译器只会在你要求它时抱怨。我的观点是它不是语言标准的有效代码,因此编译器需要发出诊断消息(在兼容模式下编译时),即使它接受代码(没有任何问题)。让编译器挑剔是件好事,因为这会告诉您不可移植的代码,您可以做出明智的决定,是保持代码原样还是重写为可移植。
        【解决方案6】:

        我正在使用下面的代码,它工作正常

        case "+": 即用双引号我得到一个错误 所以试着写

        case '+': 在单引号中

        #include <stdio.h>
        
        int main() {
            // Write C code here
            char x;
            int a=20,b=10;
            scanf("%c",&x);
            switch(x)
            {
                
            case '+':
                    printf("%d",a+b);
                    break;
            case '-':
                    printf("%d",a-b);
                    break;
            case '*':
                    printf("%d",a*b);
                    break;
            case '/':
                    printf("%d",a/b);
                    break;
            default:
                    printf("sorry");
            }
            
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多