C陷阱篇之復合表示式中的確定與不確定

2021-06-19 17:37:05 字數 1243 閱讀 6283

運算元求值順序不定

c只規定小部分運算子以已知、特定順序對其運算元求值,其他運算子的求值順序沒有定義,有偶然性。如a < b && c < d,c標準規定:先求a,如a成立,再求c並計算整個表示式的值。任何編譯器都不會先算c再算a,即&&運算子的求值順序在c標準中有明確規定,先左後右。但下到a粒度時,c編譯器對a和b的分別求值就沒有先後規定,各種可能性都有。

c裡只規定了四個運算子(&& || ,和?:號)的運算元求值順序。&&和||先求左邊運算元值,如果單靠左邊值無法決定整體值,才計算右邊。而a?b:c先求a值,再根據a值選擇b或c之一求值。運算子,先求左邊運算元的值,然後拋棄它的值,求右邊運算元值。

除此以外所有其它運算子對運算元求值順序都未定義,即不能對求值順序做任何保證。因此下面將陣列x的前n元素複製到陣列y中的方法不可行:

i = 0;

while(i < n)

y[i] = x[i++];

因為y[i]和x[i++]的求值順序不確定,如果先算x[i++],就變成y[1]=x[0],y[2]=x[1]……,反過來y[i++] = x[i];問題也是一樣。只能避開c標準的空白,用下面方法確保正確:

for(i=0; i這樣無論=號左邊還是右邊先求值,結果都一樣。

警惕&&和||中的短路運算

前面提到c裡有四個運算子的運算元求值順序確定,其中&& ||操作符先左後右,且某些情況下左邊運算的結果就能決定整體表示式的值,比如a||b,如果a為true,整個表示式肯定是true,不需要再算b。同樣對a&&b,如果a是false,整個表示式肯定是false,也不需要再算b。c標準裡這種情況右側不再求值,以節省運算量,這就是短路運算。

短路運算,即對於求值順序確定的運算子組成的復合表示式,如果計算第乙個子式能確定整個表示式的值,就不再去計算剩余子式,以此類推。這是c編譯器的特性。

之前潛規則篇從**可讀性角度不提倡用短路運算組合出複雜的長表示式,比如用ptr &&(i=ptr->value)代替if(ptr)就是自尋煩惱,前者不過形式上緊湊,不能提高效率,反而降低可讀性。

另外隱藏的短路運算還會導致bug,例如:

if (a && myfunc(b))

如果a值為0,短路計算會使myfunc(b)不被呼叫。有人忽視這點,以為myfunc(b)總會被呼叫,結果導致邏輯錯誤。因此:不要把必須執行的有效運算放在&&和||的右邊,以防止短路運算導致的功能遺漏,有效運算是指能改變程式執行結果的操作,比如ch&&i++

及a && myfunc(b)等等。

表示式中的陷阱

表示式 1.表示式是一種運算子和運算元合成在一起組成的式子 運算子需要的運算元數量不相同 a b a b a 運算子需要的運算元類別不相同 a b a 1 1 1 a 1 2.表示式都有乙個結果值 賦值運算 int a 1 a 2 system.out.println a int a 1 syste...

C 中Lambda表示式

c 語言還是比較常見的東西,這裡我們主要介紹c lambda表示式,包括介紹乙個lambda就是乙個delegate,乙個delegate指向乙個方法等方面。你有沒有接觸過c lambda表示式,有沒有覺得匿名方法這東西很不錯,減少了很多 阿,但是匿名方法的使用還並不人性化,什麼是人性化呢?比如你可...

C 中lambda表示式

在介紹lambda表示式之前,我想先介紹以下c 標準庫所支援的傳統謂詞以及傳統謂次的不足,而由此才引入的lambda表示式 謂詞是個可呼叫的表示式,其返回結果是乙個能作為條件的值。謂詞分為一元和二元,一元的可呼叫物件只能有乙個引數,二元只能有倆個引數 我們以一元謂詞舉例 c 標準庫中的find if...