「プログラミング言語C++第四版」について気が付いたことなど (7)

動かしてみると …


15. 4. 2   初期化 と 並行 処理

次 の 例 を 考えよ う:

int x = 3;
int y = sqrt(++ x);

x と y の 値 は いくつ に なる だろ う か?   その 答え は 明らか に “3 と 2 !” だ。


そうかなあ? と思って main()を書いて動かしてみると …

#include <iostream>
#include <math.h>

using namespace std;

int x = 3;
int y = sqrt(++x);

int main(int,char**)
{
  cout << "x=" << x << ", y=" << y << endl;
  return 0;
}

案の定、

x = 4, y = 2

と表示されるのであります。(Visual C++ 2013で確認)


考察:

int y = sqrt(x+1);

なら疑問の余地無く“3 と 2 !” なんですけどね。

xが最初に初期化された時点(注)では 3 なので、だから x は3だと言えなくもない気はしますけど、普通は main に飛んでくる前の初期化の細かなタイミングや順序なんて誰も気にしませんし、外部変数の初期値というのはmainに制御が渡るまでに確定している値と考えるのが妥当だと思うので、y=sqrt(++x)でxが「幾つになるだろうか」と聞かれれば、4と答えるのが正解だと私は思います。

注: 「静的に割り当てられる定数式によってオブジェクトの初期化が行なわれるのはリンク時だから、x は 3 になる」とありますが、そうとは限りません。プログラムコードを不揮発性メモリ(ROMとか)に置く必要がある環境ではリテラルや変数の初期値も一緒にROMに置く必要があります。しかしながら、ROMに置いたままでは変数の値を変更できませんから、初期値はROM上に置いておき、それをmainの前の準備処理で実際の変数の場所(RAM)にコピーするというやり方が一般的だと思います。この場合、外部結合の変数 x の初期化が行なわれるのはリンク時ではなく、プログラムの実行時、つまり main の前です。

でもそもそも、extern 変数の初期化で他のextern変数の値を変更するようなコード書いちゃ駄目ですよね。

この節ではマルチスレッド環境ではもっと色々なことが起こり得るって書いてありますが、静的か動的かには関わらず、外部結合の変数が初期化されるタイミングでは通常はメインスレッド以外は動いていませんし、もし外部結合の変数のコンストラクタで幾つかスレッドを起動していたとしても(=mainの前に走っているスレッドがあったとしても)、int y=sqrt(++x) の初期化のためのコードがそれらのスレッドの起動の都度実行されるわけではありません。外部結合の変数の初期化は一回だけです。なので、「yがsqrt(4)にもsqrt(5)にも」とはならないのではないかと思います。

むしろそれよりは、mainの前に走り出すようなスレッドでは少なくとも動的に初期化される外部結合の変数の初期化がスレッドが走り始めたタイミングで全部は終わっていない可能性があり、それはそれで危ない話ではないかと思われ、なのでコンストラクタでスレッドを起動するのはやめておいた方が無難でしょう。

 

コメントを残す