为什么 switch case 在 C++ 中的枚举类型上工作很奇怪?

问题描述 投票:0回答:2

我尝试在 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 中。有人可以解释一下为什么它会这样工作吗?

c++ debugging enums switch-statement gdb
2个回答
0
投票

即使指定了

-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

0
投票

简单的编译器优化器注意到情况 1 3 4 5 做了完全相同的事情(什么也没做),因此它将这段代码组合成单指令/单代码分支。

因此,调试器与此类优化一起使用,它无法区分这些情况,因为在单步执行此代码时,它总是通过相同的代码机。调试器符号将机器代码与单行号相匹配。

© www.soinside.com 2019 - 2024. All rights reserved.