文字列はファイル依存?コンパイラ依存?


error
warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
error C2001: 定数が 2 行目に続いています。
error C2143: 構文エラー : ';''for' の前にありません。
warning C4018: '<' : signed と unsigned の数値を比較しようとしました。

Visual C++を使っていてこんなエラーを見たことがあるだろう。

でもこのエラー、文字コードに起因することが原因というのはわかるのだが、ファイルが原因なのか、コンパイラが原因なのかがよくわからなかった。

検証

Clang、VCで以下のコードを実行してみた。
shift_jis、UTF-8、UTF-8N(BOM無し)の3種類でそれぞれファイルを保存してコンパイルを実行させている。
実行すると、文字列がコンソールに16進数で表示される。

main.cpp
#include <iostream>
// int _tmain(int argc, _TCHAR* argv[]) // <- Visual Studio
int main() {
    std::string text = "あ";
    for (int i = 0; i < text.length(); i++) {
        std::cout << std::hex << (int) text[i] << std::endl;
    }
  // system("pause"); //<-VisualStudioのみ
    return 0;
}

今回使った環境は下記

OS コンパイラ エディタ
Mac Clang XCode,CLion
Win VisualC++ VisualStudio

結果は下記

コンパイラ ファイルの文字コード 結果表示
Clang(Mac) Shift_jis ffffff82
ffffffa0
Clang(Mac) UTF-8 ffffffe3
ffffff81
ffffff82
Clang(Mac) UTF-8N ffffffe3
ffffff81
ffffff82
VC(Win) Shift_jis ffffff82
ffffffa0
VC(Win) UTF-8 ffffff82
ffffffa0
VC(Win) UTF-8N Error

ということは、Clangは、ファイルのコードをそのまま通して、VisualStudioはBOMがあればUTF-8であると認識してShiftJISに変換したうえでコンパイルしているということになる。

追記:yumetodo様より、「gccとmsvcは文字コードの変換機能がある。」と教えていただきました。

※結果表示の先頭にあるffffffは、charが1バイトに対してintが4バイトであるため。

wchar_t

文字列をwstring(wchar_t)にしてみた。

main.cpp
// 略
std::wstring text = L"あ";
// 略
コンパイラ ファイルの文字コード 結果表示
Clang(Mac) Shift_jis Error
Clang(Mac) UTF-8 3042
Clang(Mac) UTF-8N 3042
VC(Win) Shift_jis 3042
VC(Win) UTF-8 3042
VC(Win) UTF-8N Error

今度はClangの方でエラーが発生した。

CLionコンソール
Scanning dependencies of target test_unit
[ 50%] Building CXX object CMakeFiles/test_unit.dir/main.cpp.o
main.cpp:6:27: error: illegal character encoding in string literal
    std::wstring text = L"<82><A0>";
                          ^~~~~~~~
1 error generated.
make[3]: *** [CMakeFiles/test_unit.dir/main.cpp.o] Error 1
make[2]: *** [CMakeFiles/test_unit.dir/all] Error 2
make[1]: *** [CMakeFiles/test_unit.dir/rule] Error 2
make: *** [test_unit] Error 2

ようは、元の文字列をワイド文字列に変換出来ないということらしい。

エラーが無い場合すべて3042になっているのは、ワイド文字列が「1文字を1つの整数値で扱えるようにすることを目指した」ことに起因する。

ワイド文字 - Wikipedia

ちなみにUnicode対応表を見ると、UTF-16として3042が割り当てられているのがわかる。

Unicode

VCのUTF-8ファイル対応

Visual Studio で UTF-8 で C++ を書いたら心が折れそうになった件 - Hikware.Tech

こちらにあるように/source-charset:utf-8とすればutf-8を読み込めるが、なかなか悲しい現実が待ち受けています。