CodingFirst

C言語、Perl、JavaScript、最近はPythonも。出来上がったものより、プログラムを書くことが好き。あと、スイーツ。

スポンサーサイト

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

coutの<<に関する評価順(2)

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

昨日の続きで、g++(4.4)とvc10の挙動を比較してみた。
後置インクリメントに関して意外な挙動があり、g++とvc10で挙動に差が出ることが分かった。

昨日の記事→yukiocのたまに書く日記
元ネタ→はてなダイアリー

まず、元ネタの式が g++ と vc10 で結果が異なる事について。

int count=0;
cout <<count++<<" "<<count++<<" "<<count++<<" "<<count++<<" "<<endl;
  //vc10 => 0 0 0 0
  //g++  => 3 2 1 0
cout <<count<<endl; //=> 4

vc10は 0 0 0 0 となり、g++と結果が違う事が分かった。

以下は、興味本位でいろいろ試した事について。

int count=0;
cout.operator<<(count++).operator<<(endl)
    .operator<<(count++).operator<<(endl)
    .operator<<(count++).operator<<(endl)
    .operator<<(count++).operator<<(endl);
 //vc10 => 0\n0\n0\n0\n
 //g++  => 4\n3\n2\n1\n

operatorを関数っぽく書いても結果が同じ。やはり<<とoperator<<()は同じ結果になるって考えていいみたい。

int func(const int& a){
 return a;
}

int count=0;
cout
  <<func(count++)<<" "
  <<func(count++)<<" "
  <<func(count++)<<" "
  <<func(count++)<<" "
  <<endl;
 //vc10 => 3 2 1 0
 //g++  => 3 2 1 0

operator<<()を関数に書き直してみたら、vc10の挙動が変わった!

volatile int count=0;
cout <<count++<<" "<<count++<<" "<<count++<<" "<<count++<<" "<<endl;
 //vc10 => 3 2 1 0
 //g++  => 3 2 1 0
cout <<count<<endl;//=> 4

元の式に volatileを付けても挙動が変わった!

int count=0;
cout <<count++<<" "<<count++<<" "<<count++<<" "<<count++<<" "<<endl;
 //vc10 => 3 2 1 0
 //g++  => 3 2 1 0
cout <<count<<endl; //=>4
cout <<&count<<endl;

元の式の後に countのアドレスを評価する式を入れても挙動が変わった。volatile相当になったのかな?
とすると、コード生成とか最適化関係の違いっぽいなぁ...

class ref_t {
 public: ref_t& operator<<(const int& value){
  cout << value << ":" << &value << " " ;
  return *this;
 }
};

int count=0;
ref_t ref;
ref<<count++<<count++<<count++<<count++;
 //vc10 => 3:0012FF20 2:0012FF1C 1:0012FF18 0:0012FF14
 //g++  => 3:0xbf8180d8 2:0xbf8180d4 1:0xbf8180d0 0:0xbf8180cc 
 cout << endl << count << endl; //=>4

自前のoperator<<を実装し、引数を参照渡しにしてみたら、vc10が 3 2 1 0を返してきた。意外!


class val_t{
 public: val_t& operator<<(const int value){
  cout << value << ":" << &value << " " ;
  return *this;
 }
};
int count=0;
val_t val;
val<<count++<<count++<<count++<<count++;
 //vc10 => 0:0012FE44 0:0012FE44 0:0012FE44 0:0012FE44
 //g++  => 3:0xbf818094 2:0xbf818094 1:0xbf818094 0:0xbf818094
cout << endl << count << endl; //=>4

値渡しにしてみたら、vc10が 0 0 0 0 を返してきた。
vc10の << って値渡しっぽく動くって事かな?ヘッダファイルみると参照渡しっぽいけど...

volatile int count=0;
val_t val;
val<<count++<<count++<<count++<<count++;
 //vc10 => 3:0012FE38 2:0012FE3C 1:0012FE40 0:0012FE44つ
cout << endl << count << endl; //=>4

volatileにしたらやっぱりvc10が 3 2 1 0と返してくる。

これらの実行結果から分かることは...

  • count++のようなインクリメントの演算は<<の評価の前に式全体を一気に行うみたい(※)
  • その際、<<の引数は、count++と後置++なので、++する前の値を一時変数として作って渡す(※)
  • count++は文中の右から評価されるらしく、一時変数も右から作られるみたい(※)
  • 但し、vc10でcountがvolatileな演算でない場合に限り、count++の演算の前に一時変数を全部作るので挙動が異なる(※)
  • 上記のインクリメント、一時変数を作ったらやっと<<の結合と式の評価を左から行う。

だと思う。書いてて微妙だ。
※の部分は処理系依存だろうし、最適化条件とかでも結果が変わるかも知れないし。

なので、結論としては変わらず、
同じ文中でインクリメントの結果を期待するコードは処理系依存なので、避けるのが望ましい(汗)

スポンサーサイト

 | HOME |  »

Search

Recent Entries

Foot Print



Categories

Monthly

Recent Comments

Recent Trackbacks

Profile

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。