鉴于此代码:
typedef enum {
Test0 = 0,
Test1 = 1,
Test3 = 3
} TestEnum;
如果发生这种情况会怎样?
TestEnum a = 2;
switch(a) {
case Test0: println("0"); break;
case Test1: println("1"); break;
case Test3: println("3"); break;
}
我正试图在我的交换机中没有default
的情况,所以编译器会告诉我什么时候我没有处理我的枚举中的任何新值(枚举变化相当频繁并由一个提供外部图书馆)。
处理我的垃圾方法的最佳方法是什么?我可以让交换机处理该值还是会产生意想不到的后果?
很多评论/回答说没有任何反应,因为它没有处理这是(a)好消息,但(b)有点违背了编译器如何实现切换。
我了解到编译器不只是逐个查看每个选项,而是使用函数指针的查找表来实现切换。这样我们就没有其他 - 如果每个case
,我们就会到达匹配的那个。
鉴于上面的开关,编译器将创建3个看起来像这样的函数。 。
address code
1 a = 2
2 locations = [100, 200, 0, 300] // Compiler makes this lookup for us
3 goto locations[a] // This single line is our switch :)
4 carry on with the rest of the app
100 println("1")
101 goto 4
200 println("2")
201 goto 4
300 println("3")
301 goto 4
好的,所以我的简化伪代码很糟糕,但是你明白了 - 这种查找方法允许C开关为1条指令,无论选项的数量如何。
所以,我的问题是 - 如果您传入的值不在该查找数组(locations
)中会发生什么。我可以理解locations[2]
是0
所以它在运行时知道不会去那里,但如果我传入4
- 或-1
该怎么办?
答案 0 :(得分:3)
开关与任何情况都不匹配。它不会进入主体。
解决您的编辑问题。你确定你对开关编译的想法吗?我用GCC和-02
编译了这个函数 typedef enum {
Test0 = 0,
Test1 = 1,
Test3 = 3
} TestEnum;
void myfunc(TestEnum a)
{
switch(a) {
case Test0: println("0"); break;
case Test1: println("1"); break;
case Test3: println("3"); break;
}
return;
}
汇编是这样的:
.file "myfunc.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "0"
.LC1:
.string "1"
.LC2:
.string "3"
.text
.p2align 4,,15
.globl myfunc
.type myfunc, @function
myfunc:
.LFB0:
.cfi_startproc
cmpl $1, %edi
je .L3
jb .L4
cmpl $3, %edi
jne .L8
movl $.LC2, %edi
xorl %eax, %eax
jmp println
.p2align 4,,10
.p2align 3
.L8:
rep ret
.p2align 4,,10
.p2align 3
.L4:
movl $.LC0, %edi
xorl %eax, %eax
jmp println
.p2align 4,,10
.p2align 3
.L3:
movl $.LC1, %edi
xorl %eax, %eax
jmp println
.cfi_endproc
.LFE0:
.size myfunc, .-myfunc
.ident "GCC: (GNU) 4.8.2 20140206 (prerelease)"
.section .note.GNU-stack,"",@progbits
你可以看到它正在进行比较和跳跃。没有你怀疑的间接分支。
答案 1 :(得分:0)
switch语句是一种计算goto。每个case语句都是执行可以跳转的标签。您可以将评估视为(1)评估开关中的表达式以计算值,(2)将计算值与每个case语句进行比较,(3)如果找到匹配则执行跳转到那个案例陈述。
break语句的原因是提供从单个case部分到switch体末端的跳转。如果没有break语句或return语句或其他类型的跳转,执行将继续执行以下case语句。
默认情况下,您可以捕获那些个别情况与开关计算值不匹配的时间。如果您没有提供默认大小写,那么在交换机主体的末尾会添加一种隐藏的默认值作为占位符。换句话说,如果没有默认情况且没有case语句值与开关的计算值匹配,则跳过整个开关体并在闭合括号后继续执行。
默认情况的最常见用法是在没有任何案例匹配时提供默认操作。但是,默认的另一个典型用法是在切换计算值与任何case语句不匹配时捕获,以便生成ASSERT或日志。
对于switch()
的有趣用法,请参阅Duff's device for serial copy使用带有连字符的案例陈述。
在C中,枚举作为一种类型比在C ++中更松散地处理,并且基本上只是int的符号,语法糖,尽管用于表示枚举值的基础类型可能是int或更小,具体取决于编译器实现。有关C enum的基本特征,请参阅C Enumeration Specifiers。