2017-03-01 133 views
1

假設我們有這樣的代碼在C或C++:代碼中的函數執行順序如foo(bar())是什麼?

foo(bar()); 

什麼是在這種情況下,功能執行順序?

  1. 是否要求bar()首先調用,foo()被稱爲不早於bar()回報,在這種情況下foo()傳遞的bar()返回值?
  2. 是否允許編譯器重新排列上述內容,即它首先調用foo(),並延遲調用bar(),直到在foo()的代碼中foo()的參數的值實際上是需要的?

案例2可能是應該foo()這樣被定義爲優化有用:

void foo(someType par) { 
    if(someTest()) 
     baz1(); 
    else 
     baz2(par); 
} 

在這種情況下,調用bar()甚至可以完全跳過如果someTest()回報false

但是,情況2也會使程序員必須更加小心,因爲它有時會導致微妙的錯誤(例如重複發生)。

+1

bar()首先被調用。 –

+3

只要_observable behaviour_是相同的,根本就沒有函數調用。並選擇**一個**語言! C和C++是具有非常不同語義的不同語言! – Olaf

+0

類似,但更復雜的C + + 17:http://stackoverflow.com/questions/38501587/what-are-the-evaluation-order-guarantees-introduced-by-c17 –

回答

3
  1. 是否要求bar()首次調用[...]

該代碼的行爲就像是發生了什麼。

  1. 是否允許編譯器重新對上面的[...]進行重新排序?

是,如果觀察到的行爲是一樣的,如果bar()首先評價。

這就是所謂的"as-if" rule

[...]實現自由,只要結果是,如果該要求已經服從了,就可以無視這個國際標準的任何要求從該程序的可觀察行爲來確定。

+0

你確定嗎? [expr.call]的狀態*後綴表達式和參數的評估都是相互不相關的。參數評估的所有副作用在輸入函數之前進行排序* – NathanOliver

+0

@NathanOliver是的,鏈接問答中的標準引用似乎支持這一點。 – emlai

0

函數參數評估之後有一個序列點(即在任何函數的代碼被輸入之前)。因此,必須在輸入foo(...)之前評估bar()。賦予例如,在C11 standard

6.5.2.2函數調用:(10)還有就是功能指定者的評價和實際參數之後,但在實際調用之前的順序點。調用函數的每個評估(包括其他函數調用)在執行被調用函數的主體之前或之後都沒有特別排序,並且相對於被調用函數的執行而言是不確定的。

+0

請使用_valid_標準,而不是一個過時版本供參考。這並沒有說明生成的機器代碼是如何工作的。 – Olaf

0

如果你有下面的語句

foo(bar()); 

功能bar()foo()之前被調用。編譯器不能重新排序這些函數調用。

5

在C++中,[intro.execution]:

當調用一個函數(函數是否是內聯)中,用任何參數表達相關的每一個值的計算和副作用,或與所述後綴表達式指定被調用函數,在執行被調用函數主體中的每個表達式或語句之前進行排序。

bar()在開始致電foo之前必須進行評估。

C++ 17中有一些變化,表達式a(b)現在在b之前評估a而在它們未被序列化之前。在這種情況下,foo的評估不起任何作用,它只是一個標識符。但是,如果我們有foo()(bar()),那麼在調用bar()之前調用foo()將被排序,而在C++ 17之前,這兩者是不確定的。

+0

這將完成禁止內聯和完整代碼優化。如果是關於C++的話,那麼C就非常不同了。 – Olaf

+0

@Olaf不,它只是解釋了可觀察行爲是什麼。可觀察到的行爲是首先評估bar()。 – Barry

+0

「在被調用函數的主體中的每個表達式或語句的執行**之前被排序**。 - 比觀察到的行爲更進一步。 OB允許任意重新排列語句,只要OB不變。這條線明確禁止它。這可能是因爲C++具有構造函數/析構函數等。但它明確地比C中的限制更多(這個問題不幸被貼上了標籤)。 – Olaf

0

這是一樣的:

type bar_result = bar(); 
foo(bar_result); 
  1. 條()將首先被調用。
  2. bar_result作爲複製/引用傳遞。
+0

'bar()'的結果不一定要作爲副本傳遞,例如如果'foo'將其參數作爲參考。 – emlai

+0

授予,請你舉個例子嗎? –

+0

'void foo(const type&){}' – emlai

2

除了其他人所說,你可能會考慮

foo(bar1(), bar2()); 

這是一個比較有趣的情況。在此,訂單允許爲[bar1,bar2,foo]或[bar2,bar1,foo]。

相關問題