我尝试在 gdb 中调试下面的 cpp 代码,其中包含一个枚举变量并在 switch case 语句中访问它。正如您所看到的,vtype被分配为var4,因此它应该进入case var4,但它会进入case var1。当我删除枚举变量上的分配时,它会进入 case var5。
#include <iostream>
typedef enum {
var1 = 1,
var2 = 2,
var3 = 3,
var4 = 4,
var5 = 5
} VType;
int main(){
VType vtype = var4;
switch(vtype){
case var1:
break;
case var2:
std::cout << "dummy one";
break;
case var3:
break;
case var5:
break;
case var4:
//implemented elsewhere
break;
default :
std::cout << "error";
}
}
在 gdb 中,我尝试打印整数值,即 (int)vtype 和 (int)var1 它按预期显示值 1 和 4,但仍然处于 case var1 中。有人可以解释一下为什么它会这样工作吗?
即使指定了
-O0
,编译器也会注意到您的某些案例具有相同(空)主体并已将它们合并。这是来自编译器资源管理器的汇编片段。 eax 寄存器保存您的 vtype
变量。
cmp eax, 5
ja .L2 // Default case if vtype > 5
mov eax, eax
mov rax, QWORD PTR .L4[0+rax*8] // Index into the jump table below using vtype
// Note how the .L11 label appears multiple times
jmp rax
.L4:
.quad .L2 // vtype = 0: default case
.quad .L11 // vtype = 1: empty case
.quad .L7 // vtype = 2: print the "dummy" string
.quad .L11 // vtype = 3: empty case
.quad .L11 // vtype = 4: empty case
.quad .L11 // vtype = 5: empty case
.L2: // print the default string. and exit through L9. L7 is similar.
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
jmp .L9
.L11: // Do nothing, fall through
nop
.L9: // Leave the function. Common exit label for all cases.
mov eax, 0
leave
ret
简单的编译器优化器注意到情况 1 3 4 5 做了完全相同的事情(什么也没做),因此它将这段代码组合成单指令/单代码分支。
因此,调试器与此类优化一起使用,它无法区分这些情况,因为在单步执行此代码时,它总是通过相同的代码机。调试器符号将机器代码与单行号相匹配。