リンク時に配列とポインタによる型の不一致でエラーを吐いてくれるか
// a.cpp int global_array[1]; extern void access(); int main() { global_array[0]=10; // 10 というアドレス値として扱われてしまう予定 access(); }
// b.cpp extern int *global_array; void access(){global_array[0]=1; // 書き込もうとする}
各ファイルのシンタックスはwell-formedである。なぜならば配列とポインタのシンタックスの関係は
「 アドレス[添字] 」=「 *(アドレス+添字) 」
だからだ。
しかし、このコードは、ポインタと配列の違いや、コンパイラがどのようにコードを理解するかを分かっていないとされるよくある例だ。
グローバル変数を配列で定義したのにも関わらずポインタで宣言した事により、b.cppのglobal_arrayから見れば、a.cppで代入した整数値10という値は、アドレス単位の値10となるので、その解釈に則りアドレス10にアクセスしようとして、まあsegmentation faultを起こすだろう。
問題は、配列にアドレスとなる数値が格納されている事を、コンパイラは分かってくれたりするのか、またはリンカーが型の不一致でエラーを吐いてくれるのかだ。
結論としては、GCC6.1.0では吐いてくれなかった。
何故今になってこういう事を検証しているのかといえば、以前僕が使っていたコンパイラはGCC4.x.x代だったので、アップデートで色々と補強されてたりするかな〜という僅かな希望があったので、見てみたかったのだが、、、残念。
こういう問題は、いざ実行して、segmentation faultも起こさないなんていう時は余計にやっかいになる。まあ意図的でない限り、以下のようなコードは生み出されないとも思うが(そうであって欲しい)、こういった破壊的なアクセスをコンパイル時で抑制してくれればなあ...と思ったひと時であった。
// a.cpp unsigned long int global_array[1]; [[deprecated]] extern void access(); int main() { const unsigned int a=9999; global_array[0]=reinterpret_cast<unsigned long int>(&a); access(); }
// b.cpp #include<iostream> extern int* global_array; void access(){std::cout<<global_array[0]<<std::endl;}
% g++ a.cpp b.cpp test.cpp: 関数 ‘int main()’ 内: test.cpp:11:2: 警告: ‘void access()’ is deprecated [-Wdeprecated-declarations] access(); ^~~~~~ test.cpp:4:28: 備考: ここで宣言されています [[deprecated]] extern void access(); ^~~~~~ test.cpp:11:9: 警告: ‘void access()’ is deprecated [-Wdeprecated-declarations] access(); ^ test.cpp:4:28: 備考: ここで宣言されています [[deprecated]] extern void access(); ^~~~~~ % ./a.out 9999