簡體   English   中英

請解釋以下C代碼的輸出

[英]Please explain the output of following C code

代碼:

#include<stdio.h>
#include<stdlib.h>

int arr[] = {1, 2, 3, 4};
static int count = 0;

int incr( ) {
    ++count;
    ++count;
    return count;
}

int main(void)
{ 
    printf("\ncount= %d \n",count);
    int i;
    arr[count++]=incr( );
    for(i=0;i<4;i++)
       printf("arr[%d]=%d\n", i,arr[i]);
    printf("\nIncremented count= %d \n",count);
    return 0;
}

輸出量

count = 0  
arr[0]=2  
arr[1]=2  
arr[2]=3  
arr[3]=4  
Incremented count = 1  

即使全局變量計數的最終增量值為1,即使已遞增三次。

當用arr[count++]=incr( )arr[count++]=incr( )替換count++count的最終增量值為2。

這是順序錯誤導致的不確定行為。 在這行上:

arr[count++]=incr( );

(使用您的編譯器)發生的是:

  • 將arr [count]解析為arr [0],后綴++將應用於語句的末尾;
  • incr()被調用,count現在等於2,incr()返回2;
  • arr [0]被分配了2;
  • postfix ++的副作用開始了,計數現在等於1。先前對計數的更改將丟失。

通過搜索其真實姓名,您將找到有關“副作用”和“序列點”的更多信息:)

要了解代碼為什么會出錯,必須首先了解未定義的行為和順序點 ,這是一個相當高級的主題。 您還需要了解什么是未定義行為,什么是未指定行為,請在此處進行解釋

如果您對一個被視為副作用的變量執行某項操作(例如修改它),則除了計算要存儲在變量中的值之外,不允許您在下一個序列點之前再次訪問該變量。

例如, i = i++是未定義的行為,因為對同一變量有兩個副作用,並且它們之間沒有序列點。 但是i = i+1; 定義明確,因為只有一個副作用(賦值),而i+1只是用於確定要存儲什么值的讀取訪問。

在您的情況下, arr[count++]子表達式和incr()子表達式之間沒有序列點,因此您會得到未定義的行為。

這就是序列點在函數C11 6.5.2.2中的顯示方式:

在函數指示符和實際參數的求值之后,但在實際調用之前,有一個順序點。 相對於被調用函數的執行,不確定地對調用函數中所有未包括在被調用函數的主體執行之前或之后特定排序的評估(包括其他函數調用)進行排序。

這意味着函數的內容沒有相對於表達式的其余部分進行排序。 因此,您實質上是在編寫與arr[count++] = ++count;相同的表達式arr[count++] = ++count; ,除了通過該函數,您設法在操作的右側擠入了兩個未排序的++count ,否則將無法實現。 任何速率,都是未定義的行為。

通過在表達式的左手和右手之間執行序列點來修復代碼。 但是,子表達式的評估順序是未指定的行為,因此,無論首先評估左側還是右側,您都需要確保代碼是安全的。 此代碼將解決問題:

// artificial example, don't write code like this
0,arr[count++] = 0,incr();

因為逗號運算符引入了序列點。 但是,當然,您不應該編寫像這樣的廢話代碼。 真正的解決方案是永遠不要在同一表達式中將++與其他運算符一起使用。

// good code, write code like this
arr[count] = incr();
count++;

暫無
暫無

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

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