運算式 (expression、表示式、計算式)
參考
運算式
在程式內用許多的變數以及常數來儲存或是代表一些資料,
我們常常需要計算這些資料經過加減乘除或是 AND,OR
邏輯運算之後的結果,
例如:
int a, b;
float x, y;
a + b - 1;
x * (y - 5.1) / 2.7
x == y
a = 1
a && b
b | 0x1
a % 10
a++
--x
a
a > b
a <= b
1.0
-20
(a==10)||(c>0)
printf(...)
(a=1)*10
a=1, b++, c=c*d
所有的運算式都是由運算子 (operator, 算符)、
運算元 (operand)
與括號組合起來的。
運算子(算符)
在 C 程式中運算子分為:
-
算術
+(整數) -(整數) *(整數) /(整數) ++ -- +(unary) -(unary) %
+(浮點) -(浮點) *(浮點) /(浮點)
-
比較
-
邏輯
-
條件
-
其它
= sizeof (type) [](陣列存取) ()(函式呼叫) , * & ->> .
幾大類的運算子,
其中又分為二元 (binary) 的運算子及單元 (unary) 的運算子,
-
二元運算子:需要左右兩個運算元參與其運算,例如: a + b, a && b
-
單元運算子:只需要一個運算元參與其運算,例如: -a, !flag, ++b
各類運算子基本使用方法請參考課本
-
算術 pp ???~???
-
比較 pp ???~???
-
邏輯 pp ???~???
-
位元邏輯 pp ???~???
-
條件 pp ???~???
注意:比較運算是利用減法來完成的,
相關的型態轉換可以參考減法。
基本運算原則
-
值 (value)
每一個運算子會作用在其運算元上並產生一值 (value)
例如:
double a;
a=3.5 運算式的值為 3.5
a + 4 運算式的值為 7.5
int b=10;
!b 運算式的值為 0
double a=3.5;
!a 運算式的值為 0
-a 運算式的值為 -3.5
a=3.5;
a-- 運算式的值為 3.5
a=3.5;
--a 運算式的值為 2.5
-
運算順序
一個運算式中可以有多個運算子 (及其作用的運算元),
其運算之順序由
-
相同運算子的結合規則 (associativity)
-
不同運算子的優先順序 (precedence)
來決定,
請參考課本第 ??? 頁。
例如:
a=1; b=2; c=3;
結合規則:
二元加號由左至右,因此
a + b + c 運算式中先做 a + b 再做 3 + c
二元等號由右至左,因此
a = b = c 運算式中先做 b = c 再做 a = 3
優先順序:
先乘除再加減,因此 a + b * c 運算式中先做 b * c 再做 a + 6
-
小括號
運算式中運算子的運算順序可以用小括號來更改
例如: (a + b) * c 先做 a + b 再做 3 * c
-
副作用 (side effects)
大部份的運算子都不會去改變其運算元變數內儲存的數值,
可是少部份的運算子除了計算一個結果數值之外,
還會改變參與運算變數內的數值,
例如:
a = 3.5 會將變數 a 的值設為 3.5
a += 10 會將 a + 10 運算結果存入變數 a 之中
++a 會將 a + 1 運算結果存入變數 a 之中
a-- 會將 a - 1 運算結果存入變數 a 之中
注意:
-
在同一個運算式中不要讓某一變數參與兩個有 side effect
的運算以免因為 side effect 動作的順序而影響運算式的結果。
例如:
-
甚至要避免有 side effect 的變數在運算式中,例如:
-
結果無法預期 (在不同的編譯器裡可能有不同的結果),
-
建議:能避免者儘量避免。
-
共同用一個符號 (operator overloading)
C 語言中很多運算子是共同用一個符號的,
它們所代表的意義與動作不同,例如:
二元整點 + , 二元浮數 + , 單元整數 + , 單元浮點數 +
程式中都只寫個 '+' 號而已,
編譯器究竟如何分辨它們呢??
其實編譯器完全靠運算元的型態來分辨,
例如:
3.5 + 12.5 之 " + " 號為浮點,二元之加
int x; x + 3 之 " + " 號為整數,二元之加
int x; x + 3.5 之 " + " 號為浮點,二元之加
運算式中資料型態的轉換
請參考一般資料型態的轉換。
CPU 只能做同一種類數字之間的運算,
例如兩個整數的加法,
或是兩個浮點數的乘法,
如果有不同型態的資料要參予運算的話,
就必須先經過資料型態的轉換。
-
自動的 (或說隱藏的 Implicit) 型態轉換
前一個例子中
為浮點之加法,
實際上編譯器自動將整數變數 x
內存放的資料轉換為倍精準浮點數 (double),
並將常數 3.5 以倍精準浮點數 (double)表示,
如此方可執行浮點之加法。
-
強制的 (或說表面的 Explicit) 型態轉換 (type coersion)
上面範例與下例是完全一樣的
int x;
((double) x) + ((double) 3.5)
此時程式設計者為了避免混淆,
自己加入 (double) 這樣子的運算子,
確保 CPU 將變數 x 資料取出後會先將之轉換為 double 型態,
再進行浮點相加的運算。
-
C程式中何時會有隱藏的型態轉換呢??
-
算術運算式中
char 或 short 或 int + char或 short 或 int ===> int + int
long + char 或 short 或 int ===> long + long
float + float 或 double ===> double + double
-
設定運算子 '='
char x;
short 或 int 或 long 或 float 或 double y;
x = (char) y;
int x;
short 或 long 或 float 或 double y;
x = (int) y;
...
-
函式呼叫時
資料流失 (data loss)
變數內資料型態轉換時有可能造成變數內儲存的資料改變,
(精確度降低),例如:
-
浮點數轉換為整數:
float -> char, float -> int, float -> long,
double -> char, double -> int, double -> long,
都會損失所有小數點後的資料,
如果數字太大超過該整數型態可以表示的最大值時,
超過的位元也會不見,
在執行時出現這兩種狀況你的程式都會假裝沒事一樣地繼續下去,
因此你自己在寫這樣子的程式時要特別小心。
在做這樣子的型態自動轉換時,
許多編譯器都會給你警告 (warning),
告訴你資料的精確度會降低,
如果你做強制性的型態轉換的話就不會有警告訊息了。
-
倍精準浮點轉為浮點數:
小數點後的精確度會從十進位的 15 位元降為 8 位元,
此種情況不會出現任何編譯警告或是執行錯誤,
若是超過目的浮點數所能表示的最大數字的話會有執行錯誤,
程式會中斷掉。
-
long -> short 或是 long -> char 或是 short -> char
若是來源變數內的資料超過目的變數型態所能表示的最大數字的話,
不會產生任何執行時的錯誤,
但是超過的位元會自動消失,
會造成程式邏輯的錯誤。