#ふとしたコンパイルエラーから始まった
いつもどおりC言語で簡単な計算をするプログラムを書いていました。
定数をよく使うんで関数外(つまりグローバルな定数変数として)にこんなふうに書いていたんです。
const double e = 1.602e-19;
const double k_B = 1.38e-23 / e;
研究室ではMacを使っているのでAppleがカスタマイズしたclangでコンパイルしてました(gccコマンドでclangが走るようになってた)。
このプログラムがC言語の仕様を満たしているかは自分にはわかりません。
ただ自分の感覚ではコンパイルは通るだろうと思っていました。
まぁ1回目の代入ですし…
その通りclangではコンパイルが通りました。もちろん-Wall
をつけてもwarningすらもでませんでした。
ただ自宅でコンパイルするとエラーが出たのです!!!
FileName.c:xx:yy: error: initializer element is not constant
const double k_B = 1.38e-23 / e;
^
エラーメッセージはこのように出ていました。
gccのバージョンはgcc 5.3.1
でした。
そこで自宅でもclangを導入してみました。
するとやはりエラーは出ません。
gccとLLVMのclangどちらかがC言語の仕様を満たしていないのでしょうか?
それとも、C言語の仕様にはこのような記述についてそもそも定義されていないのでしょうか?
#結局どういうことなのか?
StackOverflowにこんな記事がありました。
その記事によると
C言語では定数表現には静的オブジェクトのための宣言が必要です(静的オブジェクトの宣言が
main
の前に行われるので、任意の実行時に評価する場所がありません)。
const
修飾子は明らかにconstantに関係があるように見えますが、C言語でのconst
修飾子の意味は定数式を構成できるものであり、またコンパイル時に評価ができなければなりません。つまるところconst
は、読み取り専用です。
となっています。
ほう、「任意の実行時に評価ができなければならない」と。
関数なんかのブロック外では記述してある順序によらずに評価できなければならないってことですかね?
確かにe
はconst
をつけていても変数ですから、先にe
の宣言が評価されてなければ、k_B
は決定できませんね。
はて?
ではなぜclangではエラーにならなかったのか?
clnagが仕様を満たしてないってことなのかな?
続きを読んでいくとこんなことが書いてありました。
This also means that clang (the compiler that, as I understand it, is the one invoked when you type gcc under MacOS) is very likely non-conforming, because it fails to diagnose this error. On my own Linux system, I find that, when invoked with -std=c11 -pedantic, gcc 4.7.2 correctly diagnoses the error, but clang 3.4 does not
やはり仕様を満たしていないような感じなことが書いてあるようなきがします(翻訳めんどくさくなってごめんなさい)。
うーん…
どうしたものか…
ここはgccに合わせたほうが良さそうな感じかな。
clangがこれをエラーにする可能性も十二分にあるし。
そんなわけで、あまりこんな書き方をする方はいらっしゃらないと思いますが、const
は定数じゃなくて、read onlyを意味する修飾子だってことを理解しないとダメですね。