繁体   English   中英

char arrays 上的 C/C++ 开关盒

[英]C/C++ switch case on char arrays

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

这会导致编译错误,告诉我 case 表达式不会简化为int

我如何使用大小有限的 char arrays,并将它们转换为数字类型以用于switch / case

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

在 C++ 中设置 FourCC 值

#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: ...
}

我认为这里的问题是,在 C 中, switch语句中的每个case label 必须是 integer 常量表达式。 来自 C ISO 规范,§6.8.4.2/3:

每个案例 label 的表达式应为integer 常量表达式[...]

(我的重点)

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

An integer constant expression) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. integer 常量表达式中的强制转换运算符只能将算术类型转换为 integer 类型,但作为 sizeof 运算符的操作数的一部分除外。

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

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

希望这可以帮助!

问题是switchcase分支需要一个常数值。 特别是在编译时已知的常量。 编译时不知道字符串的地址 - linker 知道地址,但甚至不知道最终地址。 我认为最终的重定位地址仅在运行时可用。

您可以将问题简化为

void f() {
    int x[*(int*)"x"];
}

这会产生相同的错误,因为"x"字面量的地址在编译时是未知的。 这不同于例如

void f() {
    int x[sizeof("x")];
}

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

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

  1. 不要将id字段设为字符串,而是将 integer 设为字符串,然后在case语句中使用常量列表。

  2. 我怀疑你需要在多个地方进行这样的switch ,所以我的另一个建议是:首先不要使用switch来执行代码,具体取决于结构的类型。 相反,该结构可以提供一个 function 指针,可以调用该指针来执行正确的printf调用。 在创建结构时,function 指针设置为正确的 function。

这是说明第二个想法的代码草图:

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语句使代码混乱。

免责声明:除非出于娱乐或学习目的,请勿使用此功能。 对于严肃的代码,使用常见的习惯用法,在一般情况下不要依赖编译器特定的行为; 如果仍然这样做,不兼容的平台应该触发编译时错误或使用良好的通用代码。


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

~/$ 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 Character Literals , Bullet 1

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

但在第二个项目符号中:

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

所以要小心。

这尤其危险,因为 alignment:在许多架构上, int是 4 字节对齐的,但字符 arrays 不是。 例如,在 sparc 上,即使此代码可以编译(它不能编译,因为直到链接时间才知道字符串地址)它会立即引发SIGBUS

我刚刚结束了使用这个宏,类似于问题或 phresnels 答案中的案例#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;
}

这比 c++ 更多 C。

联合 int_char4 { int_32 x; 字符[4] y;}

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM