从我从类似问题的各种答案中读到的内容,在某些情况下,转换案例的编译方式不同。
我有几个场景,但我不确定它们将如何编译。
采用枚举值的开关案例。案件范围从0到99并且是有序的。
采用枚举值的开关案例。这些案件的范围从0 - 30,50 - 80,100 - 150.无序。这个编译会不同于上面的场景吗?
基本上我想知道如何编译场景中的切换案例,以及两种场景之间是否存在差异。谢谢!
编辑:我应该提到我最关心的一个问题是需要多少支票来匹配案例。如果语句是线性的,那么对于方案一,如果它是if-else-如果它最多需要100次检查,除非我弄错了。但这如何处理开关盒?编译器做了哪些优化?
答案 0 :(得分:8)
一般来说,没有办法说出来。 C ++标准几乎没有对生成的代码做任何假设。这意味着只要语义保持不变,编译器几乎可以自由地(是的,有例外)重新排序代码。当我参加该课程的课程时,优化通常被称为“编译器构造的黑魔法”。
考虑到这一点,您的编译器可能会喷出相同或不同的代码。对于某些优化级别,它可能会吐出相同的代码,而对于其他优化级别则可能会完全不同它可能完全取决于您在case
块本身中使用的代码。如果你在两点有相同的代码,它可能只是优化它以使用该代码一次,或者它可能不会。
知道将会发生什么的唯一方法是查看具有某些固定标志的编译器的输出,看看会发生什么。一旦你发现了会发生什么,不要依赖它,它可能会在下一版本的编译器中发生变化。
如果您正在编程,请始终遵守语言标准为您提供的保证,并且不要假设任何其他内容。
答案 1 :(得分:5)
理论上,对于完整的切换,编译器将生成一个跳转表,即指向要直接索引的标签的指针数组(假设该值适合该范围)。
对于较不完整的版本,由编译器来平衡跳转表和分支语句。它也可能取决于设置:是否使用-O3
最大化速度或使用-Oz
最小化尺寸肯定会影响某些边缘情况。
最后,交换机只有一个案例,它可能会被表示为分支。
break
或者突破的存在不会对事情产生太大影响。程序集是根据标签定义的,break
和fallthrough都是自然表示的,因此它不应影响是否使用分支或跳转表。
演示(使用试用LLVM网站)
enum Enum {
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine
};
char const* full(enum Enum e) {
switch(e) {
case Zero: return "Zero";
case One: return "One";
case Two: return "Two";
case Three: return "Three";
case Four: return "Four";
case Five: return "Five";
case Six: return "Six";
case Seven: return "Seven";
case Eight: return "Eight";
case Nine: return "Nine";
}
return "Uh ?";
}
char const* sparse(enum Enum e) {
switch(e) {
default: return "Not handled";
case Zero: return "Zero";
case One: return "One";
case Two: return "Two";
case Seven: return "Seven";
case Eight: return "Eight";
case Nine: return "Nine";
}
}
char const* null(enum Enum e) {
switch(e) {
default: return "Not Zero";
case Zero: return "Zero";
}
}
函数full
编译为:
.text
.globl full
.align 16, 0x90
.type full,@function
full: # @full
.Ltmp0:
.cfi_startproc
# BB#0:
cmpl $9, %edi
ja .LBB0_11
# BB#1:
movl %edi, %ecx
movl $.L.str, %eax
jmpq *.LJTI0_0(,%rcx,8)
.LBB0_2:
movl $.L.str1, %eax
ret
.LBB0_3:
movl $.L.str2, %eax
ret
.LBB0_4:
movl $.L.str3, %eax
ret
.LBB0_5:
movl $.L.str4, %eax
ret
.LBB0_6:
movl $.L.str5, %eax
ret
.LBB0_7:
movl $.L.str6, %eax
ret
.LBB0_8:
movl $.L.str7, %eax
ret
.LBB0_9:
movl $.L.str8, %eax
ret
.LBB0_10:
movl $.L.str9, %eax
ret
.LBB0_11:
movl $.L.str10, %eax
.LBB0_12:
ret
.Ltmp1:
.size full, .Ltmp1-full
.Ltmp2:
.cfi_endproc
.Leh_func_end0:
.section .rodata,"a",@progbits
.align 8
.LJTI0_0:
.quad .LBB0_12
.quad .LBB0_2
.quad .LBB0_3
.quad .LBB0_4
.quad .LBB0_5
.quad .LBB0_6
.quad .LBB0_7
.quad .LBB0_8
.quad .LBB0_9
.quad .LBB0_10
将sparse
函数转换为:
.text
.globl sparse
.align 16, 0x90
.type sparse,@function
sparse: # @sparse
.Ltmp3:
.cfi_startproc
# BB#0:
movl $.L.str11, %eax
cmpl $9, %edi
ja .LBB1_8
# BB#1:
movl %edi, %ecx
jmpq *.LJTI1_0(,%rcx,8)
.LBB1_2:
movl $.L.str, %eax
ret
.LBB1_3:
movl $.L.str1, %eax
ret
.LBB1_4:
movl $.L.str2, %eax
ret
.LBB1_5:
movl $.L.str7, %eax
ret
.LBB1_6:
movl $.L.str8, %eax
ret
.LBB1_7:
movl $.L.str9, %eax
.LBB1_8:
ret
.Ltmp4:
.size sparse, .Ltmp4-sparse
.Ltmp5:
.cfi_endproc
.Leh_func_end1:
.section .rodata,"a",@progbits
.align 8
.LJTI1_0:
.quad .LBB1_2
.quad .LBB1_3
.quad .LBB1_4
.quad .LBB1_8
.quad .LBB1_8
.quad .LBB1_8
.quad .LBB1_8
.quad .LBB1_5
.quad .LBB1_6
.quad .LBB1_7
最后是函数null
:
.text
.globl null
.align 16, 0x90
.type null,@function
null: # @null
.Ltmp0:
.cfi_startproc
# BB#0:
movl $.L.str1, %ecx
testl %edi, %edi
movl $.L.str, %eax
cmoveq %rcx, %rax
ret
.Ltmp1:
.size null, .Ltmp1-null
.Ltmp2:
.cfi_endproc
.Leh_func_end0:
答案 2 :(得分:2)
大多数编译器会以不同方式编译它们。但它完全取决于编译器和架构。
答案 3 :(得分:0)
好像你只需要看看反汇编程序。切换确实非常有趣)看看有没有优化。玩得开心=)