簡體   English   中英

令人困惑的C代碼,有人為我解釋?

[英]confusing C code, someone explain it for me?

評估訂單確實很重要,因此,這是否稱為非參照透明度?

int i = 1;
int counter(){
     i = i + 1;
     return i;
}
int foo(int i, int j){
    return i*2 + 3*j;
}
int main(){
   printf("%d", foo(counter(), counter()));
}

我想你可能想到的是函數參數的評估順序在C中沒有標准化。因為counter()將在每次調用時返回不同的結果,並且foo(2, 3)的結果不同於foo(3, 2) ,編譯和執行此代碼可能會在不同平台上給出不同的結果。

然而,在同一平台上,它是確定性的,正如其他人所解釋的那樣。 [更新] (准確地說:一旦在具有特定編譯器選項的特定平台上編譯成可執行文件,所有執行都將產生相同的輸出。但是,正如評論者指出的那樣,它甚至可能在構建時在同一平台上產生不同的輸出不同的編譯選項。) [/ Update]

嚴格來說,即使使用相同的編譯器和設置在同一平台上編譯,有問題的代碼也可能會產生不同的結果。 未指定評估函數參數的順序。 C標准將“未指定的行為”定義為

使用未指明的值,或本國際標准提供兩種或更多種可能性的其他行為,並且在任何情況下都不會對其進行任何進一步的要求(C99§3.4.4/ 1)。

重要的是“在任何情況下”實現可能會做一些不同的事情,因此,例如,您的編譯器可以發出代碼,隨機選擇評估參數的順序。

顯然,在同一程序的不同運行期間,任何實現都不太可能以不同方式評估函數的參數。

關鍵是你不應該依賴於評估函數參數的順序; 在一個正確的程序中,它應該沒關系。

它是確定性的,每次都會返回相同的值。

每次調用它時, counter()都會返回一個不同的數字,因為i是全局的。 但是,全局變量僅在執行期間保持其值。 如果重新啟動程序,它將獲得值1並重新開始!

幾個答案表明,雖然不同的平台可能會給出不同的結果,但結果在給定平台上是確定性的。

這是不正確的

C99標准說(6.5 / 3表達式):

除非后面指定(對於function-call(),&&,||,?:和逗號運算符),否則子表達式的計算順序和副作用的順序都是未指定的。

因此,標准不指定調用foo()參數的評估順序。 2調用counter()的順序不能計入。 特定的編譯器可以根據以下方式對調用進

  • 要求編譯器執行的優化
  • 確切的代碼集(包括文件,翻譯單元中的略有或顯着不同的源代碼,無論如何)
  • 該計划的一周中的哪一天
  • 一個隨機數

雖然使用優化之外的其他內容不太可能,但其他編譯器選項的差異或轉換單元的差異將導致參數評估的順序不同(因為編譯器可能沒有太多理由生成不同的輸出),事實上你根本不能依賴於訂購。

事實上,每次調用foo() ,調用的評估順序都是不同的(就標准而言)。 例如,假設您的示例程序看起來像(在更明顯的時候發生了什么):

#include <stdio.h>

int i = 1;

int counter1(){
     i = i * 3;
     printf( "counter1()\n");
     return i;
}

int counter2(){
     i = i * 5;
     printf( "counter2()\n");
     return i;
}

int foo(int i, int j){
    return i + j;
}

int main(){
   int x;
   for (x=0; x<2; ++x) {
       printf("%d\n", foo(counter1(), counter2()));
   }

   return 0;
}

輸出看起來像是以下任何一種都是完全有效的(注意至少還有一種可能性):

可能性1:

counter1()
counter2()
18
counter1()
counter2()
270

可能性2:

counter1()
counter2()
18
counter2()
counter1()
300

可能性3:

counter2()
counter1()
20
counter2()
counter1()
300

編譯器每次執行代碼行時都會以不同的方式評估參數,這是可以的(即使非常奇怪),但是標准未指定順序的事實允許這樣做。

雖然評估不太可能“隨機化”,但我確實認為難以控制的事情如優化級別(或其他編譯器設置),編譯器的精確版本/補丁級別,甚至周圍的確切代碼表達式可能導致編譯器選擇不同的評估路徑。

依靠功能參數的評估順序,即使在特定的平台上,也在調整危險。

作為旁注,這是在函數中隱藏副作用是可能的事情之一的原因之一。

代碼是確定性的,但它打印的內容可能取決於編譯器,因為foo可能會收到2,3或3,2。

正如Code Clown提到的那樣,代碼是確定性的。 它會在相同的“編譯器”上給你相同的輸出。

C標准沒有指定方法調用參數的評估順序。 因此,首先調用方法foo的兩個調用中的哪一個由編譯器來決定。

函數foo()不是引用透明的。 引用透明性意味着函數應在相同輸入上調用時返回相同的值。 要做到這一點,功能必須是純粹的,即它不應該有任何副作用。

C語言不保證函數是純粹的,必須通過以下方式管理它:

  • 不將方法的形式參數存儲在本地靜態字段中
  • 不依賴於全局變量的值

(有許多方法可以使函數引用不透明,這些更常見)

這里對counter()的后續調用會產生不同的值,因此它是參考不透明的。

暫無
暫無

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

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