side effect和Sequence points

1.C11中的side effect(副作用)

  Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object.

  訪問一個易變目標、更改一個目標或文件、調用一個函數,這些操作都有副作用。副作用就是執行環境的狀態的變化。計算一個表達式通常包含值的計算和副作用的啓動。對於一個左值表達式,值計算包括確定指定目標的身份。

2.序列點

  1 The following are the sequence points described in 5.1.2.3:
  — Between the evaluations of the function designator and actual arguments in a function
  call and the actual call. (6.5.2.2).

  在函數運行和實參調用之間有序列點
  — Between the evaluations of the first and second operands of the following operators:
  logical AND && (6.5.13); logical OR || (6.5.14); comma , (6.5.17).

  在&&,||,逗號的第一和第二操作數之間有序列點
  — Between the evaluations of the first operand of the conditional ? : operator and
  whichever of the second and third operands is evaluated (6.5.15).

  在?:操作符的第一操作數和第二(第三)操作數之間有序列點
  — The end of a full declarator: declarators (6.7.6);

  在一個完整的聲明結尾處有序列點
  — Between the evaluation of a full expression and the next full expression to be
  evaluated.

  在計算完一個完整的表達式和計算下一個完整的表達式之間有序列點。

  The following are full expressions:  

    an initializer that is not part of a compound literal (6.7.9);

    the expression in an expression statement (6.8.3);

    thecontrolling expression of a selection statement (if or switch) (6.8.4);

    the controlling expression of a while or do statement (6.8.5);

    each of the (optional) expressions of a for statement (6.8.5.3);

    the (optional) expression in a return statement (6.8.6.4).          

  — Immediately before a library function returns (7.1.4).

  在一個庫函數返回之前的一刻有序列點
  — After the actions associated with each formatted input/output function conversion
  specifier (7.21.6, 7.29.2).

  在每一個格式化I/O函數的指定轉化操作之後有序列點
  — Immediately before and immediately after each call to a comparison function, and
  also between any call to a comparison function and any movement of the objects
  passed as arguments to that call (7.22.5).
  在調用一個比較函數之前和之後的一刻有序列點,在任何調用比較函數和任何傳遞給函數的參數之間有序列點

3.例子:

  3.1

  int b = 5, ++b;

  因爲","會產生序列點,所以","左邊的表達式必須先求值,如果有副作用,副作用也會生效。然後纔會繼續處理","右邊的表達式。

  3.2

  int a = 5;

  int b =  a++ > 5 ? 0:a;

  因爲"?:"處有序列點,其左邊的表達式必須先求值完畢。 a++ > 5在和5比較時,a並沒有自增,所以表達式求值爲FALSE。 因爲"?"處的序列點,其左邊表達式的副作用也要立即生效,即a自增1,變爲6。 因爲"?"左邊的表達式求值爲FALSE,所以三元操作符?:返回:右邊的值a。 此時a的值是6,所以b的值是6。

  3.3

   int i = 1;
        i++;     /* i++是一個完整表達式 */
        i++ + 1; /* i++就不是一個完整的表達式,因爲它是i++ + 1這個完整表達式的一部分 */

  3.4

  int i = 1; 

  i++, i++, i++;

  printf("%d\n", i);
  由於逗號有序列點,i=4;

  3.5

  int a = 10;
  int b = 0;
  if (b && a/b) 
  { /* some code here */ }

  因爲&&支持短路操作,必須先將&&左邊的表達式計算完畢,如果結果爲FALSE,則不必再計算&&右邊的表達式,直接返回FALSE。由於&&之前有序列點,會先判斷b,b=0會結束判斷,否則a/b會報錯。

  3.6

  奇怪的C中給出的例子。

  int i = 3;

  int b = (++i)+(++i)+(++i);

  (++i)+(++i)+(++i)之間並沒有序列點,它們的執行順序如何呢? gcc編譯後,先執行兩個++i,把它們相加後,再計算第三個++i, 再相加。

  而Microsoft VC++編譯後,先執行三個++i,再相加。 兩者得到的結果不同,誰對誰錯呢?誰也沒有錯。C標準規定:兩個序列點之間的執行順序是任意的。 當然這個任意是在不違背操作符優先級和結合特性的前提下的。 這個規定的意義是爲編譯器的優化留下空間。知道這個規定,我們就應該避免在一行代碼中重複出現被遞增的同一個變量, 因爲編譯器的行爲不可預測。 試想如果 (++i)+(++i)+(++i)換成 (++a)+(++b)+(++c)(其中a、b、c是不同的變量), 不管++a,++b和++c的求值順序誰先誰後,結果都會是一致的。

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