cout輸出流的執行順序

下面是IBM的一道筆試題

#include <iostream> 
using namespace std; 

int fun( ) 

cout << "f" ; 
return 1; 

int main() 


int i = 1; 
// cout << i++ << i++ << i++ << endl; 
cout << "m" << fun() << fun() << fun() << endl; 
return 1; 


輸出fffm111 

問題:cout這種連接寫法的輸出的執行順序是啥呢? 
cout<<"m"<<fun1()<<fun2()<<fun3()<<endl; 
<<運算是左結合的。 
必然先求cout<<"m"的值,值仍然是cout 
然後試圖求cout<<fun1()的值,這必須先求出fun1()的值。整個表達式的值仍然是cout 
然後試圖求cout<<fun2()的值,這必須先求出fun2()的值。整個表達式的值仍然是cout 
然後試圖求cout<<fun3()的值,這必須先求出fun3()的值。整個表達式的值仍然是cout 
最後是cout<<endl的值,值是cout 
整個表達式語句以分號結尾 
注意:問題就在這裏:“這必須先求出fun1()的值”,“這必須先求出fun2()的值”,“這必須先求出fun3()的值”,這3句。這是計算<<運算的前提。只要分別在計算cout<<fun1(),cout<<fun2(),cout<<fun3(),之前完成就可以了。 
因此,具體是先計算fun1()的值,還是先計算fun2()的值,還是先計算fun3()的值,還是先計算cout<<"m"的值,都不影響表達式的值。 
問題就在這裏: 
這是個<<表達式。<<本來是位運算,但是這裏cout卻是來利用運算的“副作用”。 
所謂副作用,就是計算一個表達式的時候,除了得到它的值以外,對環境產生的影響都是副作用。 
比如: 
int a=1,b=2,c=3,d; 
d=a<<b: 
這一步,a<<b計算出1左移2位得到的結果。結果是4。也就是說,賦值表達式結束後,d的值變成4,其它地方都沒有改變。這就是說這個<<運算沒有副作用。 
但是,cout<<"a"就不一樣了。這個表達式的值我們根本就不關心。我們只關心,這個表達式“計算”完以後,"a"被輸出到屏幕上了。這裏“a被輸出到屏幕上”就是副作用。 
   再看這個例子: 
int foo(int a, int b) { return a+b; } 
int bar(int a, int b) { return a-b; } 
int a=1,b=2,c=3,d; 
d=foo(a,b)+bar(b,d); 
這裏,foo()和bar()都沒有副作用。因此,這個表達式,不論是先計算foo(a,b)的值,還是先計算bar(b,c)的值,都不會影響計算的結果。 
但是,如果是這個例子: 
int foo(int* a) { (*a)++; return *a;} 
int bar(int* a) { (*a)--; return *a;} 
int a=5,b; 
b=foo(&a)+bar(&a); 
這個表達式,foo()和bar()都有副作用,所以,先計算foo(&a)還是先計算bar(&a),將直接影響到b的值。 
假如先計算foo,再計算bar。 
首先,a=5 
計算foo(&a),a變成6,foo(&a)的值是6 
計算bar(&a),a變成5,bar(&a)的值是5 
這樣,b=6+5=11 
假如先計算bar,再計算foo。 
首先,a=5 
計算bar(&a),a變成4,foo(&a)的值是4 
計算foo(&a),a變成5,bar(&a)的值是5 
這樣,b=5+4=9 
這就造成了計算結果不一致。 
   === 
那。。。怎麼辦 
一般來說,編c/c++程序有一個紀律:一個語句中不要有兩個表達式有副作用。 
典型的這類行爲包括:b=(a++)+(a++)+(a++); 
這是典型的違反這條紀律的行爲。每個a++都有副作用(改變a的值)。整個表達式的值跟求值順序直接相連。 
還有就是 
char* fun() { cout<<"q"; return ""; } 
cout<<"m"<<fun()<<fun()<<fun()<<endl; 
每個fun()都有副作用(向屏幕上顯示字符)。因此效果直接與求值順序相關。(而整個表達式的值我們根本就不關心。雖然我知道,值就是cout)。 
====== 

在c/c++中,求值順序是怎麼樣的? 

不知道。 

C/C++的規範中,求值順序是不規定的。這是爲了給編譯器以優化的空間。 

比如: 

b=(a+2)+(a+2);,那麼如果只計算一次a+2的值,而不是兩次,那麼計算量會大大降低。 

因此, 

不要在C語言裏面做這種事情: 

char* fun() { cout<<"q"; return ""; } 
cout<<"m"<<fun()<<fun()<<fun()<<endl; 

要這樣: 

char* fun() { return "q"; } 
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 輸出的一定是 "mqqq\n" 

這樣更好: 

string fun() { return string("q"); } 
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 輸出的一定是 "mqqq\n" 

這樣就更好了: 

string f="q"; // 隱式轉換 
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 輸出的一定是 "mqqq\n" 

但是這樣不好嗎?: 
cout<<"mqqq"<<endl; 
  
這應該只是個測驗。我相信IBM的軟件工程師們不會編出這種垃圾代碼的。

代碼稍微修改了下 

#include <iostream> 

using namespace std; 

int fun(int i) 

cout << "f"<<i; 
return i; 

int main() 


int i = 1; 
cout << i++ << i++ << i++ << endl; 
cout << "m" << fun(1) << fun(2) << fun(3) << endl; 
cin.get(); 
return 1; 


輸出結果: 
321 
f3f2f1m123


如果只針對題來說的話,實際是這樣的 
cout<<"m"<<fun()<<fun()<<fun(); 
對於<<其實是從右往左處理的。於是碰到fun()必然先輸出f,然後返回1,於是就變成了 
cout<<"m"<<fun()<<fun()<<1; 
繼續往左走,直到 cout<<"m"<<1<<1<<1 ;的時候已經輸出了fff ,之後就是按順序輸出了m111, 所以看到的結果就是 fffm111  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章