古いマイコンで動いていたソースコードを、
とりあえず新しいマイコン用のコンパイラに突っ込んだら、当然エラーがたくさん出てくる。
コンパイラ依存の表記などは文字列の一括置き換えでドカンとつぶしていく。
ワーニングはともかく、エラー数はかなり減ってきたところで、
よくわからないところで “unexpected …”というエラーが出てきた。
一般的には括弧の対応関係が合わないと発生するエラーだが、そうは見えない。
このような謎を解明する手段としてプリプロセッサの結果を出力するオプションを有効にした。
すると、if文の閉じ括弧の行が消失していることがわかった。
なんで? と思ってソースコードを確認してみるとこんなのだった。
if(foobar){
some(); //○○機能 }
あっ!? そういえばこのソースコードの文字コードってShift_JISだったよな。
と「能」のShift_JISの文字コードを確認すると{0x94, 0x5C}だった。
これ典型的なダメ文字じゃないか。
0x5CはASCIIコードで言えば”\”(バックスラッシュ)に相当する。
すなわち、Shift_JISを解釈する機能を持たないコンパイラにとっては
some(); //■■■■@■\
と書いてあるのと同等である。(ASCII・半角カナ範囲外の文字は「■」で代用)
それでC言語で行末の\は継続行であることを表す。
すなわち閉じ括弧の書かれた次の行もコメントに含まれてしまったのである。
とりあえずはこうやって回避した。
some(); //○○機能//←ダメ文字//
末尾が\にならなければ回避できるので適当な文字(上の例では “/”)を置けばよい。
コメント部分ならどんな文字コードでもよいと思ってたが、行末の\はダメなんですね。
ダメ文字が問題となりやすいとされるのが文字列リテラルである。
典型例として “表示” というのが “■\■ヲ” という並びになり、
文字列リテラルに\が含まれるとエスケープシーケンスに当てはめようとするが、
この場合はエスケープシーケンスに該当しないので \■ はそのまま ■ として解釈される。
結果として “■■ヲ” となり、この ■■ という部分はShift_JISで解釈しなおすと 侮 という文字になる。
というわけで “表示” が “侮ヲ” になるというのが昔はCGIなどであるあるネタだったのだという。
今回のシステムではこういう問題はない。ASCII範囲外の文字はコメントだけである。
ダメ文字問題は EUC-JP や UTF-8 では発生しない。
どちらもASCII範囲外の文字は 0x80~0xFF でエンコードされるためである。
Shift_JISは半角カナを1byteに押し込むために漢字の2byte目がASCIIコードに被らせてるんですよね。
こういう構成が問題を引き起こしかねないことは昔から知られていたが、
1byteカナの移植性を重視したShift_JISがWindowsで長らく標準的に使われていたのは知っての通り。
今だとUTF-8がいいでしょうね。漢字が3byteになっても困らないし。
というわけで移植にあたってはUTF-8に変換した方がよいという発見だった。
まさか2024年になってダメ文字に遭遇するとは思わなかった。
今までプライベートでも仕事でもダメ文字が問題になった覚えはないな。
Shift_JISを正しく解釈できる環境を使ってたのか、EUC-JPかUTF-8を使ってたのか。
どっちが主因なのかよくわかりませんけどね。どっちもありそうだな。