同一种程序逻辑,用if写没问题,用switch实现却不行?

我知道两种方式生成的汇编代码肯定不同,但是没有预想到两个程序的行为表现也不同。

  • if的版本一切正常(显示A按下X键显示BXCA按下Y键显示BYCA按下4号键B一闪而过执行return show_system_info()语句,当前页被刷新

  • switch的版本却表现为(显示A按下X键显示BXCA按下Y键显示BYCA按下4号键B一直显示,程序卡住了

附上代码和编译结果:

$ cat > hello.c <<EOF

int
show_system_info(void)
{
    for (;;) {
        putchar('A');

        // 未按键时阻塞
        // 按键后返回按键编号
        // 对于字母按键,返回 ASCII 码
        // 另有 F1 ~ F6 的按键,返回 -1 ~ 4 的整数(不是ASCII码)
        int g = get_key();

        putchar('B');
#ifdef NDEBUG
        switch (g) {
        
        // 注:可以看到每个 case 执行的都是跳转指令,
        // 因此将 break 省略。经我测试,即使加上所有
        // 的 break 语句,也会在编译器优化中被忽略掉,
        // 得到的汇编代码与 switch.s 完全相同。

        case 1:
        case 3:
            return show_freq_config(0);
        case 2:
            return show_freq_config(1);
        case 4:
            return show_system_info();
        case -1:
            return 0;
        case 0:
            continue;
        default:
            putchar(g);
        }
#else
        if (g == 1 || g == 3) {
            return show_freq_config(0);
        } else if (g == 2) {
            return show_freq_config(1);
        } else if (g == 4) {
            return show_system_info();
        } else if (g == -1) {
            return 0;
        } else if (g == 0) {
            continue;
        } else {
            putchar(g);
        }
#endif
        putchar('C');
    }
}

EOF
$ export CFLAGS='-std=c99 -Os -mtune=arm920t -mcpu=arm920t -mlittle-endian -fomit-frame-pointer -msingle-pic-base -fpic -mpic-register=r10 -msoft-float'
$ arm-elf-gcc $CFLAGS -S -c hello.c -o if.s
$ arm-elf-gcc $CFLAGS -D NDEBUG -S -c hello.c -o switch.s
$ cat if.s

    .file   "hello.c"
    .text
    .align  2
    .global show_system_info
    .type   show_system_info, %function
show_system_info:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 1, uses_anonymous_args = 0
    mov ip, sp
    stmfd   sp!, {r4, fp, ip, lr, pc}
    sub fp, ip, #4
.L20:
    mov r0, #65
    bl  putchar(PLT)
    bl  get_key(PLT)
    mov r4, r0
    mov r0, #66
    bl  putchar(PLT)
    cmp r4, #3
    cmpne   r4, #1
    movne   r3, #0
    moveq   r3, #1
    moveq   r0, #0
    beq .L19
.L4:
    cmp r4, #2
    bne .L6
    mov r0, #1
.L19:
    ldmfd   sp, {r4, fp, sp, lr}
    b   show_freq_config(PLT)
.L6:
    cmp r4, #4
    beq .L20
    cmn r4, #1
    mov r0, r4
    beq .L15
    cmp r4, #0
    beq .L20
    bl  putchar(PLT)
    mov r0, #67
    bl  putchar(PLT)
    b   .L20
.L15:
    mov r0, r3
    ldmfd   sp, {r4, fp, sp, pc}
    .size   show_system_info, .-show_system_info
    .ident  "GCC: (GNU) 3.4.6"
$ cat switch.s

    .file   "hello.c"
    .text
    .align  2
    .global show_system_info
    .type   show_system_info, %function
show_system_info:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 1, uses_anonymous_args = 0
    mov ip, sp
    stmfd   sp!, {r4, fp, ip, lr, pc}
    sub fp, ip, #4
.L18:
    mov r0, #65
    bl  putchar(PLT)
    bl  get_key(PLT)
    mov r4, r0
    mov r0, #66
    bl  putchar(PLT)
    mov r0, r4
    add r4, r4, #1
    cmp r4, #5
    addls   pc, pc, r4, asl #2
    b   .L12
    .p2align 2
.L13:
    b   .L10
    b   .L18
    b   .L6
    b   .L7
    b   .L6
    b   .L18
.L6:
    mov r0, #0
    b   .L17
.L7:
    mov r0, #1
.L17:
    ldmfd   sp, {r4, fp, sp, lr}
    b   show_freq_config(PLT)
.L12:
    bl  putchar(PLT)
    mov r0, #67
    bl  putchar(PLT)
    b   .L18
.L10:
    mov r0, #0
    ldmfd   sp, {r4, fp, sp, pc}
    .size   show_system_info, .-show_system_info
    .ident  "GCC: (GNU) 3.4.6"
阅读 3.3k
1 个回答
int get_key(){
    char a;
    a = getchar();
    getchar();
    if (a >= 'A'&&a <= 'z'){
        return (int)a;
    }
    else{
        return a - '0';
    }
}

自己写了个get_key()代码调试了一下两种方法都没有问题啊,这段代码逻辑应该是没问题的

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏