👻

コメント行があるとなぜか動くコードがあるらしい

2023/10/14に公開

コメント行があるとなぜか動くコード

以下のようなコメントを見たことはないでしょうか?

// 理由はわからないが動く
...
// よくわからないがこれがないとダメらしい
...
// ↓↓↓↓このコメントを消すと動かなくなる!絶対消すな!↓↓↓↓
...

私はエンジニア歴がそれほど長いわけではありませんが、何度か目にしたことがあります[1]
よくこれでレビュー通ったなと思いつつ、正直もうプロダクションコードに組み込まれているんだし、まあいっかと済ませてしまっています[2]

それはさておき、知人から次のようなコードを教えてもらいました。
これは「この行のコメントを消すとなぜか動かない」コードです。

#include <stdio.h>

int main(void) {
    int i = 10;

    // iの値により分岐可能
    // この行のコメントを消すとなぜか動かない
    if (i > 5) {
        printf("OK\n");
    } else {
        printf("NG\n");
    }
    return 0;
}

調べていると「文字コードの5C問題」としてちらほら出てくるので、それなりに有名なのでしょうか?
せっかくなので調べた内容をまとめたいと思います。

原因

これはコードをShift-JISで保存し、それをLinux gccでコンパイルすると発生します。
つまりというか案の定というか、文字コードが原因なのですね。

Shift-JISではの文字コードが945cであり、この5c\扱いとなります。
実際にPython (v3.11.0) で確認していると、たしかに文字コードは945cです[3]

print("能".encode("utf-8").hex()) # e883bd
print("能".encode("shift_jis").hex()) # 945c

そしてこの\はコンパイラによって扱いが異なりますが、少なくともLinux gccでは

  • 次の行もコメントとして扱う

という処理になります。
その結果、if (i > 5) {の部分がコメント扱いとなってコンパイルエラーとなるわけですね。

実際にやってみる

ここで終わってもよいのですが、せっかくなので実際にコンパイルエラーを出してみます。

まずは以下のように適当なDockerプロジェクトを作成します。
shiftjis.c, utf8.cは上述のコードをそれぞれShift-JISとUTF-8で保存したファイルです。

$ tree . -a
.
├── Dockerfile
└── src
    ├── shiftjis.c
    └── utf8.c

Dockerfileは以下です。Dockerは凝りだすと切りがないので、とにかくgccコマンドが動く環境を作ります。

FROM gcc:latest
WORKDIR /workdir

これをビルドして実行してみます。コメントアウトがそのままだと問題なく動きました。

$ docker image build -t gcc_compiler .
$ docker container run -v $(pwd)/src:/workdir gcc_compiler bash -c \
        "gcc -o output shiftjis.c && ./output"
OK

そしてコメントを削除してから再度実行すると、無事(?)エラーが出てきました。
もちろんUTF-8では問題なく動きます。

$ docker container run -v $(pwd)/src:/workdir gcc_compiler bash -c \
        "gcc -o output shiftjis.c && ./output"
shiftjis.c: In function 'main':
shiftjis.c:12:9: error: 'else' without a previous 'if'
   12 |         else
      |         ^~~~

この問題はShift-JISで末尾が5cとなる文字がコメント末尾に来ると発生します。
たとえばなどがあります。次のようなコードでも同じエラーが出ます。

int main(void)
{
    int i = 10;
    // スマートに動くコードだと業界で噂
    if (i > 5)
    { ... }
}
#include <stdio.h>

int main(void)
{
    int i = 10;
    // 嫌な予感がしているんです。そろそろなにかありそうですよ風暴
    if (i > 5)
    { ... }
}

まとめ

  • 945c () など、Shift-JISで\に該当する5cが末尾に来るコードを実行すると意図しない動作になることがある
  • 類似の文字コードを調べても、同僚によっぽどの変人がいない限り「可能」以外では起こらない問題のはず
  • UTF-8を使いましょう

補足と反省:ChatGPTに聞いてみる

この記事を書く際に上述の内容が正しいかChatGPTに質問すると、次のように反論されました。
ちなみにChatGPT4を使っています。

Shift-JIS では、「能」の文字コードは 92AB です。945c はShift-JISでの「能」の文字コードではありません。

英語の方がいいのかな?と思い

What is the character code for in Shift-JIS?

と聞いてみても

The character "能" (which means "ability" or "can" in English) has the Shift-JIS code 0x939F. You can convert it to hexadecimal representation to verify in various programming languages or tools that can handle Shift-JIS encoding.

となってしまいます。
文字コードの分野では怪しい回答になりますね。日本語特有の話だからでしょうか?

WebPilotを使って調べさせてみます。

ネット上の情報を根拠としてShift-JISにおける「能」の文字コードを調べてください。

すると次の回答がありました。今度は問題なさそうですが、コードを実行して真偽を確認できない分野は怖いですね。
本題とはズレてしまいますが反省として記しておこうと思います。

Shift-JIS(シフトJIS)は、日本語の文字を表現するための文字コードの一つです。文字「能」のShift-JISコードを調べるために、ウェブ上の情報を参照しましょう。以下の手順で進めます:

  1. WebPilotツールを使用して、ウェブ上で「Shift-JIS "能" 文字コード」に関する情報を検索します。
  2. 検索結果から関連する情報を抽出します。

(省略)

「能」の文字に関するShift-JISコードは「945C」となります。この情報は、ウェブページから取得されました。

脚注
  1. ネタかと思いきや本当に実在したので衝撃だったのを覚えています。 ↩︎

  2. 良くないですね。それでいて何気なく誰が実装したのかも調べてしまいます。仮に何か後から問題が発生しても、バグを憎んで人を憎まずの精神で行きたいものです。 ↩︎

  3. ChatGPTで調べると頑なに嘘をつかれたので(後述)疑心暗鬼になって試しました。 ↩︎

Discussion