[C言語]switch文のcase句の後にスペースが必要な場合/不要な場合

人が書いたプログラムを見ていたとき、switch文のcase句の書き方に違和感があるコードが有りました。
要点だけ抜粋すると、以下の内容です。

    switch( data ) {
            ...
        case'1':    // <- caseと'1'の間に空白がない!!
            ...
            break;
        case'2':
            ...
            break;
        default:
            ...
            break;
    }


caseと条件値の間にはスペースが必要だと思ってたのですが、上記のコードはどうやらコンパイルも正常に行え、振る舞いも想定どおりな様です。


気になったので、このへんの振る舞いに関する確認用プログラムを作成してみました。

#include <stdio.h>
#define CS4 '4'
 
int main()
{
    int data;
 
    printf( "please input data>" );
    scanf( "%d", &data );
 
    switch( data ) {
        case 0:
            printf( "pos0\n" );
            break;
        case1:      // 空白を入れない
            printf( "pos1\n" );
            break;
        case '2':   // '2' = 0x32 = 50
            printf( "pos2\n" );
            break;
        case'3':    // '3' = 0x33 = 51
            printf( "pos3\n" );
            break;
        caseCS4:    // define値で空白を入れない
            printf( "pos4\n" );
            break;
        case0x05:   // 16進数で空白を入れない
            printf( "pos5\n" );
            break;
        default:
            printf( "default\n" );
            break;
    }
}



実行結果:

$ gcc switch_test.c  -o switch_test
 
$ ./switch_test 
please input data>0
pos0
 
$ ./switch_test 
please input data>1
default
 
$ ./switch_test 
please input data>50
pos2
 
$ ./switch_test 
please input data>51
pos3
 
$ ./switch_test 
please input data>52 
default
 
$ ./switch_test 
please input data>5
default




各パターンの動作結果に対して、なぜそうなったのかを考えて見ます

case 0
    これは正常パターンなので、当然動作します。
 
case1:
    "case1"というgotoラベルと解釈されるので、正しく動作しません。
    後述しますが、アセンブラの結果を見ても"pos1"の出力処理自体が省かれています。
 
case '2':
    これも正常パターン。動作します。
 
case'3':
    これが当初違和感を感じたパターンです。
    gccでコンパイルしましたが、はやり正常に動作してます。
 
caseCS4:
    コードを書いてたときは、このパターンはいけるのでは...と思ってたのですが、NGでした。
    良く考えてみたら、"caseCS4"というgotoラベルと認識されるだけなので、ダメで当然ですね。
 
case0x05:
    これもダメでした。




で、こちらがアセンブル出力です。
pos1, pos4, pos5に関しては、処理自体が無かったものと見なされてます(コードまで追わなくても.LCxの定数部だけ見れば一目瞭然です)。
ジャンプされる可能性がないgotoラベルと見なされたからだと思われます。

$ gcc switch_test.c  -S
 
 
$ cat switch_test.s
    .file   "switch_test.c"
    .section    .rodata
.LC0:
    .string "please input data>"
.LC1:
    .string "%d"
.LC2:
    .string "pos0"
.LC3:
    .string "pos2"
.LC4:
    .string "pos3"
.LC5:
    .string "default"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $.LC0, %eax
    movl    %eax, (%esp)
    call    printf
    movl    $.LC1, %eax
    leal    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    __isoc99_scanf
    movl    28(%esp), %eax
    cmpl    $50, %eax
    je  .L4
    cmpl    $51, %eax
    je  .L5
    testl   %eax, %eax
    jne .L7
.L3:
    movl    $.LC2, (%esp)
    call    puts
    jmp .L8
.L4:
    movl    $.LC3, (%esp)
    call    puts
    jmp .L8
.L5:
    movl    $.LC4, (%esp)
    call    puts
    jmp .L8
.L7:
    movl    $.LC5, (%esp)
    call    puts
    nop
.L8:
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
    .section    .note.GNU-stack,"",@progbits




というわけで、直感に反する振る舞いは”caseの後に空白をおかずに文字定数が続く”パターンだけでした。
この振る舞いになる原因をJIS X3010の規格書から調べてみたのですが、分かりませんでした…
なぜだろう??

関連記事

コメントを残す

メールアドレスが公開されることはありません。