char数组上的C / C ++开关案例

时间:2011-08-16 19:09:54

标签: c arrays casting switch-statement

我有几个数据结构,每个数据结构都有一个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");
}

这会导致编译错误,告诉我案例表达式不会减少到int

如何使用有限大小的char数组,并将它们转换为数字类型以在switch / case中使用?

7 个答案:

答案 0 :(得分:4)

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

Set a FourCC value in C++

#define FOURCC(a,b,c,d) ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) )

对每个标识符使用枚举类型或宏可能是个好主意:

enum {
    ID_SQRT = FOURCC( 's', 'q', 'r', 't'),
    ID_LOG2 = FOURCC( 'l', 'o', 'g', '2')
};

int structure_id = FOURCC( structure->id[0], 
                           structure->id[1],
                           structure->id[2],
                           structure->id[3] );
switch (structure_id) {
case ID_SQRT: ...
case ID_LOG2: ...
}

答案 1 :(得分:2)

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

  

每个案例标签的表达式应为整数常量表达式 [...]

(我的重点)

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

  

整数常量表达式)应具有整数类型,并且只能有操作数   它是整数常量,枚举常量,字符常量,sizeof   结果为整数常量的表达式,以及浮点常量    即时的演员阵容。 仅在整数常量表达式中强制转换运算符   将算术类型转换为整数类型,但作为sizeof的操作数的一部分除外   操作

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

直观地说,原因可能是在某些实现中,生成的可执行文件中字符串的实际位置不一定在链接之前指定。因此,如果标签依赖于间接依赖于这些字符串地址的常量表达式,则编译器可能无法为switch语句发出非常好的代码,因为它可能会错过编译跳转表的机会,因为例。这只是一个例子,但规范中更严格的语言明确禁止你做你上面描述的事情。

希望这有帮助!

答案 2 :(得分:2)

问题是case的{​​{1}}分支期望值恒定。特别是,在编译时已知的常量。编译时不知道字符串的地址 - 链接器知道地址,但不知道最终地址。我认为最终的重定位地址仅在运行时可用。

您可以将问题简化为

switch

这会产生相同的错误,因为在编译时不知道void f() { int x[*(int*)"x"]; } 文字的地址。这与例如不同。

"x"

由于编译器知道指针的大小(32位构建中的4个字节)。

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

  1. 不要将void f() { int x[sizeof("x")]; } 字段设为字符串而是整数,然后在id语句中使用常量列表。

  2. 我怀疑你需要在多个地方做case这样的事情,所以我的另一个建议是:首先不要使用switch执行代码依赖关于结构的类型。相反,该结构可以提供一个函数指针,可以调用该函数指针进行正确的switch调用。在创建结构时,函数指针被设置为正确的函数。

  3. 这是一个代码草图,说明了第二个想法:

    printf

    有了这个,您的原始代码就可以:

    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 ); } 语句来混淆代码。

答案 3 :(得分:2)

免责声明:除了娱乐或学习目的外,请勿使用此功能。对于严肃的代码,使用常用的习惯用法,在一般情况下从不依赖编译器特定的行为;无论如何,不​​兼容的平台应该触发编译时错误或使用良好的通用代码。


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

~/$ 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!

编辑:哦,看看,在语法摘录的正下方有 2.13.2 字符文字子弹1

  

[...]包含多个c-char的普通字符文字是多字符文字。多重的 -      ter literal具有int类型和实现定义的值。

但是在第二个子弹中:

  

[...]包含多个c-chars的宽字符文字的值是实现定义的。

所以要小心。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

我刚刚结束使用这个宏,类似于问题中的案例#3或phresnels的答案。

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

答案 6 :(得分:0)

这比C ++更多。

union int_char4 {int_32 x; char [4] y;}

一个union声明,定义它的成员从同一个地址开始,实质上为同一组字节提供不同的类型。

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

因为,你想学习,实施取决于你。