切换大小写:大小写标签不减少为整数常量

时间:2019-04-08 19:06:44

标签: c switch-statement

我非常清楚"/addChangeVIEW.fxml"语句背后的机制以及为什么需要整数常量。我不理解的是为什么以下switch标签不被视为整数常量。之后怎么样了?一个不存在的变量?谁能分类? C编译器真的真的很笨吗?

case

当然……

struct my_struct {
    const int my_int;
};

switch (4) {

case ((struct my_struct) { 4 }).my_int:

    printf("Hey there!\n");
    break;

}

编辑以回答尤金的评论:

  

您真正的用例是什么?如果它是整数常量,为什么要使其变得如此复杂?

我试图找到一个巧妙的技巧,使用error: case label does not reduce to an integer constant case ((struct my_struct) { 4 }).my_int: 而不是union在两个字符的字符串之间切换,如以下示例所示:

struct

...当然不会编译。

在我的机器上#include <stdio.h> union my_union { char my_char[sizeof(int)]; int my_int; }; void clever_switch (const char * const my_input) { switch (((union my_union *) my_input)->my_int) { case ((union my_union) { "hi" }).my_int: printf("You said hi!\n"); break; case ((union my_union) { "no" }).my_int: printf("Why not?\n"); break; } } int main (int argc, char *argv[]) { char my_string[sizeof(int)] = "hi"; clever_switch(my_string); return 0; } ((union my_union) { "hi" }).my_int26984((union my_union) { "no" }).my_int。但是我不能自己写这些数字,因为它们取决于机器的字节序(显然,我的机器是低字节序的)。但是编译器知道后者,并且在编译期间确切知道28526的数字。

恼人的是,我可以已经做到了,但是只使用了非常晦涩(效率稍低)的语法。以下示例可以正常编译:

((union my_union) { "no" }).my_int

所以问题仍然存在:C编译器(或本例中的C标准)真的真的很笨吗?

5 个答案:

答案 0 :(得分:5)

虽然表达式this.props.securityMode在运行时确实被评估为4,但它不是常数。开关盒需要一个常量表达式。

我看到您已经将((struct my_struct) { 4 }).my_int声明为my_int。但这仅意味着以后不能对其进行修改。这并不意味着表达式const是常量。

如果您使用((struct my_struct) { 4 }).my_int而不是if-statement,就可以了。

switch-case

答案 1 :(得分:4)

case语句中的switch标签需要一个整数常量表达式,其定义为:

  

整数常量表达式应具有整数类型,并且仅应具有整数常量,枚举常量,字符常量,sizeof表达式(其结果是整数常量),_ Alignof表达式和浮点常量(它们是强制转换的立即数)的操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,除非作为操作数的一部分转换为sizeof或_Alignof运算符。

表达式((struct my_struct) { 4 }).my_int不能被该定义视为整数常量表达式,即使它是一个整数值表达式,其值也可以在编译时确定。

答案 2 :(得分:1)

这是最低分母。

C标准说((struct my_struct) { 4 }).my_int不满足对大小写标签施加的约束(即它们是整数常量表达式),因此不需要兼容的C编译器足够聪明就能对其进行优化。

该标准不会禁止编译器对其进行优化。 确实,优化是clang所做的。

您的程序:

#include <stdio.h>
struct my_struct {
    const int my_int;
};

int main()
{
    switch (4) {

        case ((struct my_struct) { 4 }).my_int:

            printf("Hey there!\n");
            break;

    }
}

just works on clang,但是如果使用-pedantic进行编译,则会收到警告。

在其他情况下,例如在区分VLA和常规数组时,整数常量表达式与其他整数表达式之间的区别也会影响其他基于switch或基于goto的构造,如果它们被禁止跳到VLA的范围内。同样,只要至少进行了一次诊断,编译器就可以折叠它并允许这种跳转(c警告折叠,而不是跳转)。

如果您确实使用了这些构造,并且编译器不会阻止您,则您的程序将无法移植。

最后,在某些情况下,整数的编译时常数也会影响类型。

我相信Linux内核使用类似于

#define IS_CEXPR(X) _Generic((1? (void *) ((!!(X))*0ll) : (int *) 0), int*: 1, void*: 0)

检测整数常量表达式(我听说这是清除VLA的任务的一部分)。

这是基于C标准规则的,即等于0的整数常量表达式强制转换为(void*)是一个空指针常量,而一个正则表达式则强制转换为(void*)只是一个空值指针,即使该表达式的值已知为0也是如此。确定三元数类型的规则然后在(void*)表达式和空指针常量之间进行区分,导致(1? (void *) ((!!(X))*0ll) : (int *) 0)的类型为{{ 1}},如果int *是整数常量表达式,则X是整数常量。

大多数编译器可能不会这么轻松地解决类型系统冲突(尤其是在void *内部)。

答案 3 :(得分:1)

关于为什么的问题,C标准不允许编译器接受

case ((struct my_struct) { 4 }).my_int:

...我们无法确定地回答这一问题,因为C委员会中没有人(据我所知),而且这个设计决定是30年前做出的,因此没有人有很大的机会谁在那里记住基本原理。

但是我们可以说这些话:

  1. 原始的1989 C标准有意遗漏了许多可能已经实现的功能,但是它们在实现复杂性,编译时内存需求等方面付出了巨大的代价。例如,标准中“常量表达式”和“ 整数常量表达式”之间区别的最初理由是,编译器永远不需要在编译时进行浮点运算。

    您所要求的功能与

    一样难以实现
    static const int CONSTANT = 123;
    ...
    switch (x) { case CONSTANT: ... }
    

    也不需要在C中工作(尽管在C ++中)。

  2. 自1989年以来,对C标准的添加相对较小,并且仅是对大量需求的回应。特别是,据我所知,“实现此功能不再昂贵”已不足以作为理由。

这是我能给你的最好答案。

答案 4 :(得分:0)

好吧。为什么不可能看起来已经被彻底解释了... ...那我就把它留在这里。

case ((int)((struct my_struct) { 4 }).my_int):

clang 9,arch-linux,x86_64