我有几个数据结构,每个数据结构都有一个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
中使用?
答案 0 :(得分:4)
遵循使用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: ...
}
答案 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个字节)。
现在,如何解决您的问题?我想到了两件事:
不要将void f() {
int x[sizeof("x")];
}
字段设为字符串而是整数,然后在id
语句中使用常量列表。
我怀疑你需要在多个地方做case
这样的事情,所以我的另一个建议是:首先不要使用switch
执行代码依赖关于结构的类型。相反,该结构可以提供一个函数指针,可以调用该函数指针进行正确的switch
调用。在创建结构时,函数指针被设置为正确的函数。
这是一个代码草图,说明了第二个想法:
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数组的第一个字节的指针。
因为,你想学习,实施取决于你。