C/C++;字符数组上的开关大小写

C/C++;字符数组上的开关大小写,c,arrays,casting,switch-statement,C,Arrays,Casting,Switch Statement,我有几个数据结构,每个都有一个4字节的字段 因为在我的平台上,4个字节等于1int,所以我想在case标签中使用它们: switch (* ((int*) &structure->id)) { case (* ((int*) "sqrt")): printf("its a sqrt!"); break; case (* ((int*) "log2")): printf("its a log2!"); break; case (((int) 'A')<<

我有几个数据结构,每个都有一个4字节的字段

因为在我的平台上,4个字节等于1
int
,所以我想在
case
标签中使用它们:

switch (* ((int*) &structure->id)) {
   case (* ((int*) "sqrt")): printf("its a sqrt!"); break;
   case (* ((int*) "log2")): printf("its a log2!"); break;
   case (((int) 'A')<<8 + (int) 'B'): printf("works somehow, but unreadable"); break;
   default: printf("unknown id");
}
switch(*(int*)&structure->id)){
案例(*(int*)“sqrt”):printf(“它是一个sqrt!”);break;
案例(*(int*)“log2”):printf(“它是log2!”);break;

案例((int)a)

这比C++更为C。

并集int_char4{int_32 x;char[4]y;}

联合声明、定义其成员以相同的地址开始,本质上为同一组字节提供不同的类型

int_char4 ic4; ic4.x是int,ic4.y是指向char数组第一个字节的指针


因为,您想学习,实现取决于您。

我认为这里的问题是,在C中,
开关
语句中的每个
大小写
标签必须是整数常量表达式。根据C ISO规范,§6.8.4.2/3:

每个箱子标签的表达式应为整数常量表达式[…]

(我的重点)

然后,C规范将“整数常量表达式”定义为常量表达式,其中(§6.6/6):

整型常量表达式)应为整型,且只能有操作数 它们是整数常量、枚举常量、字符常量、sizeof 结果为整数常量的表达式,以及作为 强制转换的立即操作数。整数常量表达式中的强制转换运算符只能 将算术类型转换为整数类型,但作为操作数的一部分转换为sizeof的情况除外 操作员。

(我再次强调)。这意味着您不能在
case
语句中将字符文本(指针)强制转换为整数,因为在整数常量表达式中不允许该强制转换

直观地说,这可能是因为在某些实现中,在链接之前不必指定生成的可执行文件中字符串的实际位置。因此,如果标签依赖于一个常量表达式,而该常量表达式依赖于例如,它可能会错过编译跳转表的机会。这只是一个示例,但规范中更严格的语言明确禁止您执行上述操作


希望这会有所帮助!

问题在于
开关的
大小写
分支需要一个常量值。特别是一个在编译时已知的常量。字符串的地址在编译时是未知的-链接器知道地址,但甚至不知道最终地址。我认为最终的、重新定位的地址只有用可以在运行时使用

你可以把你的问题简化为

void f() {
    int x[*(int*)"x"];
}
这会产生相同的错误,因为编译时不知道
“x”
文本的地址

void f() {
    int x[sizeof("x")];
}
因为编译器知道指针的大小(32位构建中有4个字节)

现在,如何解决您的问题?我想到两件事:

  • 不要将
    id
    字段设置为字符串而不是整数,然后在
    case
    语句中使用常量列表

  • 我怀疑您需要在多个位置执行这样的
    开关
    ,因此我的另一个建议是:首先不要根据结构的类型使用
    开关来执行代码。相反,该结构可以提供一个函数指针,可以调用它来执行正确的
    printf
    调用e创建结构时,函数指针设置为正确的函数

  • 下面是说明第二个想法的代码草图:

    struct MyStructure {
       const char *id;
       void (*printType)(struct MyStructure *, void);
       void (*doThat)(struct MyStructure *, int arg, int arg);
       /* ... */
    };
    
    static void printSqrtType( struct MyStructure * ) {
       printf( "its a sqrt\n" );
    }
    
    static void printLog2Type( struct MyStructure * ) {
       printf( "its a log2\n" );
    }
    
    static void printLog2Type( struct MyStructure * ) {
       printf( "works somehow, but unreadable\n" );
    }
    
    /* Initializes the function pointers in the structure depending on the id. */
    void setupVTable( struct MyStructure *s ) {
      if ( !strcmp( s->id, "sqrt" ) ) {
        s->printType = printSqrtType;
      } else if ( !strcmp( s->id, "log2" ) ) {
        s->printType = printLog2Type;
      } else {
        s->printType = printUnreadableType;
      }
    }
    
    有了它,您的原始代码就可以执行以下操作:

    void f( struct MyStruct *s ) {
        s->printType( s );
    }
    

    这样,您就可以将类型检查集中在一个地方,而不是用大量的
    switch
    语句将代码弄乱。

    由于对齐,这尤其危险:在许多体系结构上,
    int
    是4字节对齐的,但字符数组不是。例如,在sparc上,即使此代码可以编译(这是不可能的,因为字符串地址在链接时间之前是未知的)它会立即引发
    SIGBUS

    遵循使用FourCC代码进行视频编码时使用的确切方法:

    免责声明:除非出于好玩或学习的目的,否则不要使用此选项。对于严肃的代码,请使用常见的习惯用法,在一般情况下不要依赖特定于编译器的行为;否则,不兼容的平台应触发编译时错误或使用良好的通用代码


    根据语法,该标准似乎允许使用多字符字符常量。但尚未检查以下内容是否真的合法

    ~/$ cat main.cc
    
    #include <iostream>
    
    #ifdef I_AM_CERTAIN_THAT_MY_PLATFORM_SUPPORTS_THIS_CRAP
    int main () {
        const char *foo = "fooo";
        switch ((foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | (foo[3]<<0)) {
        case 'fooo': std::cout << "fooo!\n";  break;
        default:     std::cout << "bwaah!\n"; break;
        };
    }
    #else
    #error oh oh oh
    #endif
    
    ~/$ g++ -Wall -Wextra main.cc  &&  ./a.out
    main.cc:5:10: warning: multi-character character constant
    fooo!
    
    ~/$cat main.cc
    #包括
    #如果我确定我的平台支持这些废话
    int main(){
    const char*foo=“fooo”;
    
    switch((foo[0]我最后使用了这个宏,类似于问题或短语答案中的案例3

    #define CHAR4_TO_INT32(a, b, c, d) ((((int32_t)a)<<24)+ (((int32_t)b)<<16) + (((int32_t)c)<<8)+ (((int32_t)d)<<0)) 
    
    switch (* ((int*) &structure->id)) {
       case (CHAR4_TO_INT32('S','Q','R','T')): printf("its a sqrt!"); break;
    }
    

    <代码>定义Cal4xtoIIt32(a,b,c,d)(((in 32)t)a)是C++(如题题)或C99(如在标签中)我不确定答案是不是两个不同,但是在没有明确的理由的问题中看到两种不同的语言是令人困惑的。现在标题中有两种语言,其中一种在标签中重复。我们是不是从这一点推断出你根本就不讲C++?为什么它仍然在标题中?不是一个EndiaFRI。我看到的是Endy代码?为什么要编写令人困惑、可能会中断、不可移植的代码?只需使用分派表即可。在C99中,精确的32位有符号整数类型命名为
    int32\u t
    ,而不是
    int\u 32
    。我认为这并不能解决OP的原始问题。您能详细说明一下吗?如何在case表达式中使用并集?因此,它们会减少到恒定的表达式
    ~/$ cat main.cc
    
    #include <iostream>
    
    #ifdef I_AM_CERTAIN_THAT_MY_PLATFORM_SUPPORTS_THIS_CRAP
    int main () {
        const char *foo = "fooo";
        switch ((foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | (foo[3]<<0)) {
        case 'fooo': std::cout << "fooo!\n";  break;
        default:     std::cout << "bwaah!\n"; break;
        };
    }
    #else
    #error oh oh oh
    #endif
    
    ~/$ g++ -Wall -Wextra main.cc  &&  ./a.out
    main.cc:5:10: warning: multi-character character constant
    fooo!
    
    #define CHAR4_TO_INT32(a, b, c, d) ((((int32_t)a)<<24)+ (((int32_t)b)<<16) + (((int32_t)c)<<8)+ (((int32_t)d)<<0)) 
    
    switch (* ((int*) &structure->id)) {
       case (CHAR4_TO_INT32('S','Q','R','T')): printf("its a sqrt!"); break;
    }