Edited at

C++の微妙に環境依存なコードとか

More than 3 years have passed since last update.

 OSや開発環境の仕様の違いうんたらで、気づいたら環境依存のコードになってた個人的メモ。


戻り値の無い関数

 クラスのメンバー関数はどこからも呼び出されていなければ、戻り値がなくてもVS2013ならコンパイル可能、ただのバグだと思う。

グローバルな関数だと戻り値が無ければ普通にエラー。

 実装忘れてた関数があって、XCodeでコンパイルしようとしたらエラーになった。


class test
{
bool 戻り値の無い関数()
{

}
}


数学系関数

 sinやらcos等はstd::名前空間は環境によって無くても良かったり、良くなかったり。

多分Cの数学ライブラリだと名前空間不要なのでそのあたりの兼ね合いっぽい。

とりあえずstd::は省略しない方が良い。


int main()
{
double a = cos(1.0);
return (int)a;
}


安全性を向上させた関数

sprintf等をVS2013でコンパイルしようとすると、sprintf_sに置き換えろと警告が出るので、_sが付いた関数に書き換えたりします。

しかし、その辺りの関数はMSVCの独自拡張だったりするので、sprintf自体を使うなと言う事になる。

#include <stdio.h>


int main()
{
char a[10];
sprintf_s(a,10, "あ");
return 0;
}


識別子に使える文字

 環境によって使える文字がバラバラ。識別子に日本語等を使う場合注意が必要。

[MSVC]C#並に大体の文字が使える、絵文字とかもOK。実験的実装を行っている。

[Clang]規格準拠っぽい。

[gcc]アルファベットと数字とかだけ。規格非準拠。

 Clangでコンパイル通ったのにAndroid開発環境のアセンブラがエラーを吐くパターンもありました。

 自分が遭遇した例だと、上下左右より↑↓←→の方が見やすいと思って変数名に使ったら、規格的にはNGでした。

 この辺りはMSVCというか規格のバグのような気もします。


int main()
{
int abc = 0;//どの環境でもOK
int = 0;//gccではエラー
int = 0;//clangでエラー、MSVCは実験的実装によりOK
}


ファイル名に使える文字

 OSによって違います。

 MacやWindowsは大文字小文字を区別しませんが、GNU/Linuxは区別するので注意が必要。include時の大文字小文字はファイル名に合わせましょう。

 日本語ファイル名はエンコードがややこしいので使わない方が良いでしょう。

あとAndroidは画像等のリソースファイル名に大文字が使えません。

 なので、ソースファイル名はアルファベット大文字、小文字とアンダーバーのみ。

画像ファイル名はアルファベット小文字とアンダーバーのみ、とかにしときましょう。

 ヘッダーファイルとかだとコンパイルエラーになるので良いけど、画像ファイル名とかが間違ってたり制限にかかると、何で実行時エラー???みたいになってハマります。


ソースコードの文字エンコーディング

 MSVCデフォルトのShift-JISでエンコードしてて、他環境で文字化けみたいなパターンがあります。

 MSVCの場合、ソース内にShift-JISに無くてUTF-8にある文字を入れておけばUTF-8(BOM有り)で保存されるため大体安心です(オプションで確認は消せる)著作権表記を©にしておくと違和感無いですね。

//Copyright© hogehoge 2015~

 しかしMSVCだとUTF-8(BOM無し)は対応してないっぽく、XCodeでUTF-8(BOM無し)等で保存したり改行コードが違ったりすると、エンコードを変更しなおす必要が出たりします。MSVC以外を使う人がUTF-8(BOM有り)にして改行コードを合わせるか、ツールで変換して使うなりする感じで良いと思う。

 関係無いですが、昔は文字エンコードがShift-JISだとコメントの末尾を改行と解釈して次の行を無視してどうのこうのみたいのがありましたが、最近の環境だとそういうエラーが起こらないようなコンパイルオプションがデフォルトでオンになっているようです。


リテラルの文字エンコーディング

文字リテラルのエンコードは環境によって違うので注意。

MSVCはShift-JISが標準で他はUTF-8が標準なので、UTF-8に統一するのが楽ですMSVC用に一行書いときましょう。

MSVC以外だと無視されます。

setlocaleもしといた方が良いです。


#pragma execution_character_set("utf-8")

int main()
{
std::setlocale(LC_ALL, "Japanese");
}


ファイル入出力

 環境や実行方法によって、実行ファイルの直下だったりプロジェクトのディレクトリだったり、ファイルの出力先や入力元が変わります。

 ディレクトリ操作系の関数は環境によって統一されてない感じなので、boost::filesystemとか使えば良いと思う。標準入りはよ。


#include <fstream>

int main()
{
std::ofstream ofs( "hogehoge.txt" );

ofs << "test" << 123 << std::endl;

return 0;
}


乱数

 Cのrand()関数はclangとgccは同じアルゴリズムらしいので同じ数値が得られますが。

MSVCは互換性の関係からか線形合同法使ってるらしく、違う値が得られて、結果も偏ります。

c++の"random"関連なら、同じシードから同じ値が得られます。そっちを使いましょう。


#include <stdio.h>
#include <stdlib.h>

int main()
{
int count[4] = {0,0,0,0};

for (int a = 0; a < 1024*256; ++a)
{
count[rand() % 4]++;
}
//MSVCだとあからさまに偏ってる
printf("%d,%d,%d,%d\n", count[0],count[1],count[2],count[3]);
}


まとめ

 大体の面倒毎は文字コードですね、移植するまで問題に気付け無いし。

なおメイン環境がclangやgccでMSVCでビルドする必要が出た場合は他にも気をつける事がある模様。