簡體   English   中英

為什么不同的C ++編譯器對此代碼給出不同的結果?

[英]Why do different C++ compilers give different results for this code?

我正在編寫一些C ++代碼來娛樂和練習,以了解有關語言功能的更多信息。 我想更多地了解靜態變量及其在遞歸函數中的行為。 在g ++編譯器中嘗試此代碼,可以得到預期的結果:

#include <iostream>
using namespace std;

int f(const int& value)
{
   static int result = 0;
   return result += value;
}

int main()
{
   cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10)));
   return 0;
}

但我的朋友在Microsoft Visual C測試相同的代碼++ 6.0輸出為50, 80, 90我與其他C ++編譯器進行了測試(G ++,Borland公司,代碼::塊和MinGW下的Linux,Win和Mac的)產量為110, 100, 40 我不明白輸出怎么可能是50, 80, 90 ...

為什么MSVC的輸出不同?

未指定以下三個子表達式的求值順序:

f(10)
f(f(10))
f(f(f(10)))

編譯器可以按任何順序評估那些子表達式。 您不應依賴程序中特定的求值順序,尤其是當您打算使用多個編譯器進行編譯時。

這是因為該表達式中的任何地方都沒有序列點。 唯一的要求是在需要結果之前(即在打印結果之前)評估這些子表達式中的每個子表達式。

在您的示例中,實際上有幾個子表達式,在這里我將其標記為通過k:

//   a  b     c       d  e f      g       h  i j k
cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10)));

operator<<acdgh )的調用都必須按順序進行求值,因為每個調用都取決於上一個調用的結果。 同樣,必須先評估b才能評估a ,並且必須先評估k才能評估jih

但是,這些子表達式之間不存在依賴關系: b的結果不依賴於k的結果,因此編譯器可以自由地生成先評估k然后bb然后k

有關序列點以及相關的未指定和未定義行為的更多信息,請考慮閱讀Stack Overflow C ++ FAQ文章“未定義行為和序列點” (您的程序沒有任何未定義行為,但是本文中的大部分內容仍然適用)。

僅僅因為輸出在屏幕上從左到右顯示,並不意味着評估順序遵循相同的方向。 在C ++中, 未指定函數參數的求值順序。 另外,通過<<操作符打印數據僅僅是調用函數的語法。

簡而言之,如果您說operator<<(foo(), bar()) ,則編譯器可以先調用foobar 這就是為什么調用具有副作用的函數並將其用作其他函數的參數通常不是一個好主意的原因。

一個簡單的方法來確切地了解它在做什么:

int f(const int& value, int fID)
{
   static int result = 0;
   static int fCounter = 0;
   fCounter++;
   cout << fCounter << ".  ID:" << fID << endl;    
   return result += value;
}

int main()
{
   cout << f(10, 6) << ", " << f(f(10, 4), 5) << ", " << f(f(f(10, 1),2),3);
   return 0;
}

我同意其他人在回答中所說的話,但這將使您確切地看到它在做什么。 :)

前綴運算符語法被轉換為以下前綴表示法:

<<( <<( <<( cout, f(10) ), f(f(10)) ), f(f(f(10))) )
 A   B   C

現在有三個不同的函數調用,在上面標識為A,B和C。 每個調用的參數為:

     arg1        arg2
A: result of B, f(10)
B: result of C, f(f(10))
C: cout       , f(f(f(10)))

對於每個調用,允許編譯器以任何順序評估參數,為了正確評估A的第一個參數,必須首先評估B,並且類似地,對於B的第一個參數,整個C表達式必須進行評估。 這意味着第一個參數依賴項要求執行A,B和C時有部分順序。 每個調用和兩個自變量的求值也存在部分排序,因此必須在B之前求B 1和B 2 (指的是調用B的第一和第二自變量)。

這些部分排序不會導致對調用的執行有獨特的要求,因為編譯器可以在嘗試評估第一個參數之前決定執行所有第二個參數,從而導致等效路徑:

tmp1 = f(10); tmp2 = f(f(10)); tmp3 = f(f(f(10)));
cout << tmp1 << tmp2 << tmp3;

要么

tmp3 = f(f(f(10))); tmp2 = f(f(10)); tmp1 = f(10);
cout << tmp1 << tmp2 << tmp3;

要么

tmp2 = f(f(10)); tmp1 = f(10); tmp3 = f(f(f(10)));
cout << tmp1 << tmp2 << tmp3;

或...繼續結合。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM