第 3 講:運(yùn)算符
運(yùn)算符是什么?
=
叫做賦值運(yùn)算符,它被稱為一個(gè)廣義的運(yùn)算符,因?yàn)樗鼘?shí)際上并不稱為一種運(yùn)算,而是一種行為:從微觀角度來(lái)看,它是把符號(hào)右側(cè)的數(shù)據(jù)結(jié)果,放到變量 i
存放的位置上;從宏觀來(lái)看,其實(shí)就是使得 i
得到 5 的這個(gè)結(jié)果的行為,就稱為賦值(Assignment)。
實(shí)際上,不僅是加號(hào),在 C 語(yǔ)言里,我們提供了很多執(zhí)行操作,稍后我們將挨個(gè)寫(xiě)出來(lái)。
表達(dá)式是什么?
表達(dá)式(Expression),就是一個(gè)計(jì)算式子,這個(gè)式子擁有一個(gè)結(jié)果。從目前來(lái)看,它可以和 printf
、scanf
等語(yǔ)句來(lái)講獨(dú)立開(kāi)來(lái)。表達(dá)式最終有一個(gè)數(shù)值結(jié)果,并且這個(gè)結(jié)果可以被其它地方所利用(當(dāng)然你也可以不用它)。比如
2 + 3
就是一個(gè)表達(dá)式,而表達(dá)式的結(jié)果就是 5,我們利用了這個(gè)結(jié)果,賦值給了 i
實(shí)際上沒(méi)有任何問(wèn)題,表達(dá)式是可以單獨(dú)成句的,但是這樣一來(lái),得到的 5 也沒(méi)有任何意義。
運(yùn)算符一覽
數(shù)學(xué)運(yùn)算符
第一個(gè)種類的運(yùn)算符稱為數(shù)學(xué)運(yùn)算符(Math Operator),它用來(lái)表達(dá)一些數(shù)學(xué)運(yùn)算期間產(chǎn)生的運(yùn)算符號(hào)。我們暫且先不說(shuō) sin
等函數(shù)符號(hào),我們?cè)?C 語(yǔ)言里先暫時(shí)不當(dāng)成運(yùn)算符,而只關(guān)注于那些用非字母構(gòu)成的符號(hào),比如 +
、-
等等。
數(shù)學(xué)運(yùn)算符在 C 語(yǔ)言里提供了如下一些:
正號(hào)(
+
)負(fù)號(hào)(
-
)加號(hào)(
+
)減號(hào)(
-
)乘號(hào)(
*
)除號(hào)(
/
)取模運(yùn)算(
%
)
首先是正負(fù)號(hào)。正負(fù)號(hào)都只需要在符號(hào)右側(cè)添加一個(gè)變量名稱,或一個(gè)數(shù)值常量,表示這個(gè)數(shù)的本身,或這個(gè)數(shù)的相反數(shù)。例如
順便提一下,這里的正負(fù)號(hào)都只需要為其旁邊添加一個(gè)數(shù)字作為計(jì)算即可,所以這樣的運(yùn)算符稱為一元運(yùn)算符或單目運(yùn)算符(Unary Operator)。
下面來(lái)說(shuō)一下,需要兩個(gè)數(shù)字參與計(jì)算的這五個(gè)基礎(chǔ)的計(jì)算符號(hào)了:加減乘除模。這些符號(hào)稱為二元運(yùn)算符或雙目運(yùn)算符(Binary Operator),因?yàn)樾枰獌蓚€(gè)數(shù)字才能起作用。
加減乘三個(gè)運(yùn)算符直接用就可以了。比如下面的例子:
那么這里就需要提及一點(diǎn)了。不同的數(shù)據(jù)類型運(yùn)算是具有不同的精度的,比如小數(shù)就有精度一說(shuō),而整數(shù)沒(méi)有。在 C 語(yǔ)言里,如果你運(yùn)行好了程序,系統(tǒng)將會(huì)為你默認(rèn)顯示 6 個(gè)小數(shù),所以輸出的結(jié)果就是注釋里給出的那個(gè)樣子;當(dāng)然,
double
類型還能為你顯示 12 位小數(shù),于是你就會(huì)看到一大堆的 0。如果你要控制小數(shù)位數(shù)的顯示的話,請(qǐng)使用前文介紹的顯示小數(shù)位數(shù)的格式化字符串來(lái)處理。比如%.2f
就表示小數(shù)點(diǎn)后只會(huì)兩位。
當(dāng)然,數(shù)據(jù)類型進(jìn)行混用也是可以的。比如一個(gè)整數(shù)加一個(gè)小數(shù),在 C 語(yǔ)言里是允許的,不過(guò)從微觀來(lái)看,執(zhí)行起來(lái)沒(méi)有那么簡(jiǎn)單。
那么請(qǐng)你思考這個(gè)問(wèn)題,result1
和 result2
分別都是多少呢?這里給出答案,result1
是 3,而 result2
是 3.5。實(shí)際上是很好理解的:由于計(jì)算的限制,一個(gè)整數(shù)和一個(gè)小數(shù)計(jì)算的時(shí)候,肯定會(huì)往“范圍更大”的方向去處理。比如 int
類型顯然不可能讓它無(wú)限制的大,這樣就使得數(shù)字一定有一個(gè)取值范圍,而不是像人腦那樣無(wú)限制的大。實(shí)際上,float
的范圍比 int
的更大,所以為了保證計(jì)算結(jié)果能夠放得下,結(jié)果一定是 類型來(lái)存儲(chǔ)的,否則 int
類型就可能會(huì)出現(xiàn)存不下,進(jìn)而出錯(cuò)的問(wèn)題。
現(xiàn)在考慮到這個(gè)的時(shí)候,a
和 b
的求和結(jié)果肯定就是 float
類型所表示的 3.5 了。但你仔細(xì)觀察代碼,就可以發(fā)現(xiàn),result1
可是 int
類型的,這意味著這個(gè)它想把 float
的結(jié)果用 int
表示出來(lái),所以最終 3.5 被程序截?cái)啵ㄗ⒁?,并不是四舍五入),于是這個(gè)數(shù)就變?yōu)榱?3;同理,第二個(gè)則完全不需要轉(zhuǎn)換,所以 result2
一定是 3.5 這個(gè)結(jié)果。
那么,這里就學(xué)習(xí)到了一個(gè)知識(shí)點(diǎn),叫做類型轉(zhuǎn)換(Type Conversion)。類型轉(zhuǎn)換分兩種,一種是人為強(qiáng)制定義這個(gè)結(jié)果轉(zhuǎn)換為另一種類型的模式,叫做強(qiáng)制類型轉(zhuǎn)換或顯式轉(zhuǎn)換(Explicit Conversion);而另外一種,就是上面這樣,通過(guò)系統(tǒng)自己的機(jī)制進(jìn)行的類型轉(zhuǎn)換,叫做隱式轉(zhuǎn)換(Implicit Conversion)。
但是,可以發(fā)現(xiàn),不論類型較小轉(zhuǎn)較大,還是較大轉(zhuǎn)較小,C 語(yǔ)言貌似都給出了隱式轉(zhuǎn)換的模式和手段。比如
float
轉(zhuǎn)int
就是典型的大轉(zhuǎn)小,而它采用的手段是截?cái)?/strong>(Truncate),而不是四舍五入(Round),所以在使用這種轉(zhuǎn)換的時(shí)候應(yīng)小心數(shù)據(jù)類型不同時(shí)引發(fā)的一些潛在的問(wèn)題。
接下來(lái)是除法和求模運(yùn)算。除法需要大量使用類型轉(zhuǎn)換的知識(shí)點(diǎn),所以需要你注意和小心每一步的執(zhí)行邏輯了。除法和數(shù)學(xué)上有些不同,因?yàn)?C 語(yǔ)言有整數(shù)和小數(shù)的區(qū)分制度,所以除下來(lái)的結(jié)果不總是和數(shù)學(xué)相同的。
如果除號(hào)兩邊的數(shù)都是整數(shù)(int
、long
等),結(jié)果一定是整數(shù);如果兩邊的數(shù)不都是整數(shù)的話(至少有一個(gè)是小數(shù)),結(jié)果都應(yīng)為小數(shù)。
這句話什么意思呢?
可以看到,這里舉出的例子都是一些比較奇特的使用方式,它展示出了所有除法需要注意的地方。
最后我們來(lái)看下求模運(yùn)算。
取模運(yùn)算即帶余除法下的余數(shù)數(shù)值。但和數(shù)學(xué)不同的是,C 語(yǔ)言的取模運(yùn)算的兩邊的數(shù)字,都必須是整數(shù)才可以進(jìn)行計(jì)算。
如果除法下有一個(gè)負(fù)數(shù)值的話(注意負(fù)數(shù)也是整數(shù)),結(jié)果取決于被除數(shù)的符號(hào)
-
和減法 -
都是一樣的。包括前面的運(yùn)算符也是一樣。
賦值運(yùn)算符
接下來(lái)我們來(lái)看一些賦值運(yùn)算符。
普通賦值(
=
)自增自減運(yùn)算符(
++
和--
)復(fù)合賦值運(yùn)算符(
op=
)
普通的賦值運(yùn)算符在前文里其實(shí)已經(jīng)說(shuō)了,所以這里大可不必去講。不過(guò)這里還需要說(shuō)一種情況。
可以看到,這個(gè)等號(hào)在這里和數(shù)學(xué)意義就完全不同了。在編程里,=
表示把右側(cè)的結(jié)果賦值給左邊,即 a
變量的內(nèi)存區(qū)域存放的是原始的數(shù)值再加 5 的新結(jié)果。所以從宏觀來(lái)看,這樣的寫(xiě)法就好比讓變量自己增大 5 個(gè)單位。
當(dāng)自增為 1 或自減為 1 的時(shí)候,我們可以使用自增自減的單目運(yùn)算符,來(lái)操控變量增大或減少一個(gè)單位。比如
++
b
應(yīng)該是多少呢?這就牽扯到了自增自減運(yùn)算符的前綴和后綴寫(xiě)法的不同了。
自增自減運(yùn)算符是為了將變量自身的數(shù)值增大或減小一個(gè)單位而特別定義出來(lái)的。因?yàn)榇蠖鄶?shù)計(jì)算操作之中都會(huì)使用到這一點(diǎn),所以C語(yǔ)言單獨(dú)提供了這樣的一種東西。
另外,自增自減運(yùn)算符也可以寫(xiě)到變量前面。不過(guò)意義不同?,F(xiàn)在來(lái)闡述一下。
所以這個(gè)題的結(jié)果為 4444
才對(duì)。你做對(duì)了嗎?
我們知道,編程之中的等號(hào)和數(shù)學(xué)上的等號(hào)的意義不同,編程的等號(hào)實(shí)際是一個(gè)操作,將右側(cè)的數(shù)據(jù)的數(shù)值結(jié)果拿到左側(cè)變量的位置上存放。所以C語(yǔ)言之中,a = a + 1
這種寫(xiě)法才得以存在(將 a
原來(lái)的數(shù)值加 1,得到的新數(shù)值再重新放到 a
變量的存儲(chǔ)位置上存放)。不過(guò),這樣的寫(xiě)法有些丑陋,因?yàn)榇_實(shí)等號(hào)兩側(cè)又有相同的變量名字,寫(xiě)法也太長(zhǎng),所以才出現(xiàn)了復(fù)合賦值運(yùn)算符一說(shuō)。
復(fù)合賦值運(yùn)算符是說(shuō),省略右側(cè)相同的變量名,并把右側(cè)的運(yùn)算符號(hào)放到等號(hào)左側(cè)拼在一起寫(xiě)的特殊規(guī)范。比如:
前文的 op=
的 op
表示的是運(yùn)算符的符號(hào),這里可以替換為任何運(yùn)算符,包括稍后講到的其他一些運(yùn)算符。
比較運(yùn)算符
比較運(yùn)算符是用作數(shù)值比較之用的,每一個(gè)表達(dá)式都會(huì)產(chǎn)生一個(gè)特定的數(shù)值結(jié)果表征判斷結(jié)果?;镜谋容^運(yùn)算符一共有以下一些:
>
(大于)、<
(小于)、>=
(大于等于)、<=
(小于等于)、==
(等于)、!=
(不等于)。
前兩個(gè)就不用說(shuō)了,表示大于和小于;中間兩個(gè)分別是大于等于和小于等于,只是因?yàn)閿?shù)學(xué)的符號(hào)無(wú)法直接在代碼里面打出來(lái),所以才用兩個(gè)符號(hào)拼在一起表示的這樣的含義。第5個(gè)是等號(hào),判斷兩個(gè)數(shù)是否相同;最后一個(gè)則是不等號(hào),注意不等號(hào)的寫(xiě)法是感嘆號(hào)和等號(hào)拼接的意思。
這樣賦值是沒(méi)有問(wèn)題的,剛才說(shuō)過(guò),右側(cè)會(huì)得到一個(gè)特定的數(shù)值來(lái)表征判斷結(jié)果。首先2是不大于3的,所以a > b
是錯(cuò)誤的,在C語(yǔ)言之中,錯(cuò)誤的結(jié)果會(huì)被轉(zhuǎn)為數(shù)字0進(jìn)行數(shù)據(jù)處理;正確的則會(huì)被轉(zhuǎn)為1,所以c
的值為0,而d
的值為1。而后面的a != a
肯定也不對(duì),所以e
的值為0。
邏輯運(yùn)算符
邏輯運(yùn)算用于處理高中學(xué)到的命題和邏輯那樣的東西,它提供了三種邏輯表達(dá):
&&
(且)、||
(或)、!
(非)
數(shù)學(xué)上的且、或、非的符號(hào)寫(xiě)法在代碼里面不好輸入,所以只能用上面的三種寫(xiě)法代替。它最后也是一個(gè)表達(dá)式,也會(huì)產(chǎn)生一個(gè)數(shù)值,表征最終的判斷結(jié)果。和上面一樣,對(duì)的則會(huì)為轉(zhuǎn)為 1,錯(cuò)的則會(huì)轉(zhuǎn)為 0。不過(guò),如果是人為定下的數(shù)字來(lái)直接表示判斷結(jié)果的話,非 0 的數(shù)值都會(huì)化為“正確的”,而 0 則會(huì)化為“錯(cuò)誤的”。
首先,因?yàn)?6 直接作為邏輯判斷的一邊來(lái)充當(dāng)條件的話,因?yàn)槭欠?0 數(shù)值,所以會(huì)被轉(zhuǎn)化為 1 處理(6 表示“正確的”,作為數(shù)值處理后為 1),而 b
是 0,作為判斷的另一邊的話,所以應(yīng)當(dāng)表示“錯(cuò)誤的”,作為數(shù)值處理即 0,所以且的關(guān)系是同真才真,有假必假。所以最終a && b
表達(dá)式應(yīng)當(dāng)是“錯(cuò)誤的”,最后的值是 0,所以 c
為 0。
d
用的是或,同假才假,有真必真。所以 6 是“正確的”數(shù)值,所以整體表達(dá)式為真。故賦值時(shí),表達(dá)式用 1 表示正確的。所以 d
為 1。
e
是非 a,而 a 表示正確的,所以非 a 是錯(cuò)誤的,故 e
= 0。
特別注意
邏輯運(yùn)算符有一個(gè)特征,比如且的關(guān)系,如果有假就假,所以表達(dá)式符號(hào) &&
的兩邊,如果左側(cè)已經(jīng)為假,意味著整個(gè)表達(dá)式一定為假,那么右側(cè)的結(jié)果是不會(huì)測(cè)算的。如果帶有計(jì)算式子,右側(cè)結(jié)果不會(huì)被處理和計(jì)算。
注意,++a
此時(shí)不會(huì)運(yùn)算,即 a
不會(huì)自增1,原因是 b
為 0,已經(jīng)意味著整個(gè)表達(dá)式錯(cuò)誤,后者不會(huì)被計(jì)算。
同理,或 ||
也是一樣。
最后這里作出一個(gè)總結(jié):

位運(yùn)算符
計(jì)算位運(yùn)算符
位運(yùn)算是將數(shù)據(jù)值變?yōu)?0 和 1 的二進(jìn)制數(shù)處理的方式。一共提供了四種操作:
&
(且)、|
(或)、~
(非)、^
(異或)
它具有和邏輯運(yùn)算差不多的性質(zhì),不過(guò)是對(duì)于二進(jìn)制數(shù)位操作處理的。處理時(shí),是對(duì)位處理。比如:
另外,這里的異或需要提一下,異或表示“要么……要么……”,選其中任意一個(gè)才可以,都選和都不選都不行,換句話說(shuō),都選或都不選(1 ^ 1 = 0 ^ 0 = 0),任選其一都即可(0 ^ 1 = 1 ^ 0 = 1)。
移位運(yùn)算符
移位運(yùn)算符是將數(shù)值用二進(jìn)制處理后,所有數(shù)值結(jié)果全往左或往右移動(dòng)的結(jié)果。寫(xiě)法有這兩種:
>>
(右移)、<<
(左移)
結(jié)論:右移多少位表示將數(shù)據(jù)縮小 ?倍,左移多少位表示將數(shù)據(jù)擴(kuò)大
?倍。其中的
?表示取整操作,舍棄小數(shù)部分。
最后,所有的位運(yùn)算符號(hào)也具有復(fù)合賦值運(yùn)算符。比如實(shí)際運(yùn)用之中,有這樣的用途:
語(yǔ)句表示不借助其他變量交換變量存儲(chǔ)的數(shù)據(jù)值。
a = 2, b = 3
或
另外還有一種交換變量的方式是這樣的:
也可以。甚至縮寫(xiě):