本節書摘來自華章出版社《c++程式設計教程(第3版)》一書中的第2章,第2.4節基本運算符和表達式,作者張志航,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視
2.4 基本運算符和表達式
2.4.1 c++運算符及表達式簡介
在c++中,資料處理是通過運算來實作的。運算符又稱為操作符。為表示一個計算過程,需要使用表達式,表達式是由運算符、運算量構成的一個計算序列。在c++中有很多運算符,如算術運算符(+、-、*、/、%)、關系運算符(>、>=、<、<=、==、!=)等。表2-4中列出了c++中各種運算符及其優先級和結合性。
表2-4 c++中的運算符及其優先級和結合性

若一個運算符隻能對一個運算量進行,則稱其為一進制運算符或單目運算符,如表2-4中第2行的“–”(取負)運算符。若一個運算符需要兩個運算量,則稱其為二進制運算符或雙目運算符,如表2-4中第4行的“+”(加法)和“–”(減法)運算符。若一個運算符需要3個運算量,則稱其為三元運算符或三目運算符,如表2-4中第13行的“?:”條件運算符,後續章節會介紹其意義。
2.4.2 算術運算符和算術表達式
c++提供了5個算術運算符,它們是+(加)、-(減)、*(乘)、/(除)、%(求餘),都是二進制運算符,其中+(正号)和-(負号)又可用作一進制運算符。
值得注意的是,對于除法運算,當兩個運算量均為整型量時為整除,整除的意義是取除法結果的整數部分,如5/2得到結果2。當至少有一個運算量為實型量時則為通常意義的除法,如5.0/2得到結果2.5。
對于求餘運算(或稱模運算),運算量必須為整型量。例如,8%3結果為2,而8.0%3為非法運算。
2.4.3 運算優先級和結合性
c++表達式中可以出現多個運算符和運算量,計算表達式時必須按照一定的次序,運算符的優先級和結合性規定了運算次序。
所謂優先級是指,若在同一個表達式中出現了不同級别的運算符,首先計算優先級較高的。不同的運算符具有不同的運算優先級。表2-4中所列出的運算符的優先級自上而下是遞減的。例如,表達式d=a+b c中出現了3個運算符,即=(指派)、+(加法)、(乘法),這3個運算符的優先級由高到低依次是*、+、=,是以先算乘法,再算加法,最後執行指派運算,即将指派運算符右邊的表達式的計算結果賦給變量d。
括号可以改變運算的優先次序,如表達式d=(a+b) c的運算次序是+、、=。
所謂結合性是指,在表達式中多個優先級相同的運算符連續出現時,運算次序是“自左向右”還是“自右向左”。表2-4列出了各運算符的結合性。例如,表達式d=a+b-c中出現了3個運算符,即=(指派)、+(加法)、-(減法),其中加法和減法的運算優先級相同,而指派運算的優先級較低。從表2-4中看出,加法和減法運算的結合性是從“左向右的”,是以計算該表達式時,先計算加法,再計算減法,最後進行指派。而表達式a=b=c中出現的兩個運算符優先級相同,運算的結合性是“自右向左”,是以先進行最右側的指派,再進行左邊的,詳見2.4.8節。
2.4.4 關系運算符和關系表達式
“關系運算符”實際上就是“比較運算符”。關系運算符的意義及其運算優先次序如下:
關系表達式是由關系運算符連接配接兩個表達式構成的,如a>3.0、a+b>b+c、(a=3)>(b=5)、 'a'<'b'等。
關系運算符的運算優先級比算術運算符的優先級低,但比指派運算符的優先級高。參加關系運算的兩個操作數可以是任意類型的量。當比較結果成立時,結果為1(表示“真”,即true),當比較結果不成立時,結果為0(表示“假”,即false)。例如,若有“int a=1,b=2,c=3;”,則表達式a>b的值為0;表達式b<a+c的值為1;表達式a==b-1的值為1;表達式c>b>a的值為0,因為按照運算符的結合性,首先計算c>b結果為1,再計算1>a,結果為0。
2.4.5 邏輯運算符和邏輯表達式
c++語言中提供了3個邏輯運算符,如下所示。
! 邏輯“非”(一進制運算符)
&& 邏輯“與”(二進制運算符)
|| 邏輯“或”(二進制運算符)
若a和b是兩個運算量,a&&b的意義是,當a和b均為真時,表達式的值為真。a || b的意義是,當a和b均為假時,表達式的值為假。!a的意義是,當a為真時,!a為假;當a為假時,!a為真。可以用如表2-5所示的“真值表”來概括。
表2-5 邏輯運算“真值表”
接兩個表達式構成的,如a>b&&x>y、a==b || x==y、!a>b。3個邏輯運算符的優先級由高到低依次是!、&&、||。邏輯非運算符(!)的優先級比算術、關系運算符高,而“與”(&&)和“或”(||)的運算優先級比算術、關系運算符的優先級低,但比指派運算符的優先級高。是以,上述3個邏輯表達式的意義等價于(a>b)&&(x>y)、(a==b)||(x==y)、(!a)>b。
參加邏輯運算的運算量可以是任意類型的資料。進行邏輯運算時,在判斷運算量的邏輯真假性時,規定任何非0值表示邏輯真,0值表示邏輯假。例如,若a=-1、b=2.0,則a為真,b也為真,進而表達式a&&b的結果為真。
c++在給出關系表達式或邏輯表達式運算結果時,以數值1代表“真”,以數值0代表“假”。例如,若a=1、b=-2,則表達式a>b的值為1。
注意:在c++程式中,要表示數學關系0≤x≤10時,邏輯表達式必須寫0<=x && x<=10,而不能寫成0<=x<=10。因為c++語言中表達式的運算是按照優先級和結合性進行的,不能看成與數學意義相同。例如,當x=-1時,數學關系0≤x≤10顯然是不成立的;而c++中表達式0<= x<=10的運算結果為真,與數學關系式沖突。計算過程是:表達式0<= x<=10中兩個運算符的優先級相同,按照結合性,自左向右運算,先計算0<=x,結果為0;再計算0<=10,結果為真。而對表達式0<=x &&x<=10,先計算0<=x,結果為0;再計算0&&x<=10,結果為0,與數學關系保持一緻。注意在計算表達式0&&x<=10時,涉及邏輯運算優化問題,見2.4.11節的叙述。
2.4.6 位運算符和位運算表達式
位運算是對整型量的運算,并且規定符号位參與運算,主要用于編寫系統軟體,完成彙編語言能夠完成的一些功能,如對機器字以及機器字中的二進制位進行操作。位運算符共有6個,它們是按位與(&)、按位或(|)、按位異或(^)、按位取反(~)、左移(<<)和右移(>>)。下面逐一介紹。
1.按位與(&)
;再計算0運算符“&”将兩個運算量的對應二進制位逐一按位進行邏輯與運算。每個二進制位(包括符号位)均參加運算。例如:
char a=3, b=-2, c; // 此時,可将a、b、c看成1位元組長度的整型量
` c = a & b;
運算結果:變量c的值為2。
2.按位或(|)
運算符“|”将兩個運算量的對應二進制位逐一按位進行邏輯或運算。每個二進制位(包括符号位)均參加運算。例如:
char a=18, b=3, c; // 此時,可将a、b、c看成1位元組長度的整型量
` c = a | b ;
運算結果:變量c的值為19。
3.按位異或(^)
運算符“^”将兩個運算量的對應二進制位逐一按位進行邏輯異或運算。每個二進制位(包括符号位)均參加運算。異或運算的含義是,若對應位相異,結果為1;若對應位相同,結果為0,例如:
char a=18, b=3, c; // 此時,可将a、b、c看成1位元組長度的整型量
` c = a ^ b ;
^ b 0000 0011
運算結果:變量c的值為17。
請讀者思考:如下程式段執行後,a、b的值分别是多少?參見本章習題第12題。
`int a=5, b=9;
a = a^b;
b = a^b;
4.按位取反(~)`
運算符“~”是一進制運算符,作用是将運算量的每個二進制位逐一取反。例如:
`int a=18, b;
~ a 0000 0000 0000 0000 0000 0000 0001 0010
運算結果:變量b的值為-19,或記為十六進制數0xffffffed。
5.左移(<<)
設a、n是整型量,左移運算的一般格式為:a<例如,已知“int a=15, x;”,a的二進制值是0000 0000 0000 1111,做“x=a<<3;”運算後x的值是0000 0000 0111 1000,其十進制值是120。對一個量進行左移一個二進制位,相當于乘以2操作。左移n個二進制位,相當于乘以2n操作。程式運作時,左移n位比乘以2n操作速度快。
左移運算有溢出問題,因為若以補碼表示,整型量的最高位是符号位,當左移一位時,若符号位不變,則相當于乘以2操作,但當符号位變化時,就發生了溢出。例如,若有“char a=127, x ;”即a的二進制值是0111 1111,做“x=a<<2;”運算後x的值為1111 1100,它的真值-4,此時即發生了溢出。原因是,8位二進制補碼所能表示的數值的範圍是-128~+127,a的初值是127,若将a的值乘以22,則超出了數值範圍。溢出後,變量a的值是其在記憶體中實際存儲的值1111 1100,它是一個在邏輯上不正确的值。因為127乘以22後,邏輯上應得到508,但實際上是-4,這就是“溢出”的後果。“溢出”是存儲位的限制引起的。
6.右移(>>)
設a、n是整型量,右移運算的一般格式為:a>> n,其意義是将a按二進制位向右移動n位,移出的最低n位舍棄,高位補0還是1呢?這取決于a的資料類型,若a是有符号的整型量,則高位補符号位;若a是無符号的整型量,則高位補0。例如:
`char a = -4, b=4, x, y;
x = a >> 2;
y = b >> 2;
a: 1111 1100 → x: 1111 1111
b: 0000 0100 → y: 0000 0001
x的值是-1,y的值是1。右移一位相當于除以2操作。又例如:
unsigned char a = -4 , x ;
a: 1111 1100 → x: 0011 1111`
x的值是63。此時,右移一位符号位發生了變化,稱為溢出,就不表示除2操作了。
說明:上面的例子中對a進行左移或右移,a本身的值并沒有發生變化。這類似于:
int a=1, b;
b = a+2;
執行後a本身的值并沒有變化。
請思考:已知“unsigned a; int n=4;”,表達式“a=(a<>(16-n))”對a中存儲的二進制值做了怎樣的操作?
2.4.7 自增、自減運算符和表達式
c++中有兩個可以改變變量自身值的運算符,它們是自增(++)和自減(--)運算符,作用是使變量自身的值加1或減1。這兩個運算符均是一進制運算符,而且隻能對變量做運算。它們可以放在變量之前或之後,如++i、--i表示先将i的值加1或減1,然後再參加其他運算;而i++、i--表示先用i的值參加運算,然後再将變量i的值加1或減1。
例如:“int i=3, j; j=++i;”,則運算結束後,i 的值是4,j的值也是4。
又如:“int i=3, j; j=i++;”,則運算結束後,i 的值是4,j的值是3。
再如:“int i=3, j=4, x; x=(i++)+(j++);”,則運算結束後,i、j的值是4、5,而x的值是7。
注意:自增、自減運算符隻能作用于變量,表達式3++或++(x+y)都是非法的,原因是在這兩個表達式中,++作用在常量和表達式上,而常量和表達式是不能被修改的。
2.4.8 指派運算符和指派表達式
1.指派運算符
在c++中,“=”是指派運算符,指派表達式是由指派運算符連接配接一個變量和一個表達式構成的,其格式是:
<變量> <指派運算符> <表達式>
如:a=8和a=b+c是兩個合法的指派表達式。
指派表達式的求解過程是:求出<表達式>的值,賦給<變量>。
整個指派表達式(帶下劃線的)的值是:<變量>擷取的值。
注意:指派表達式中<表達式>還可以是另一個指派表達式,如a=b=5。指派運算符“=”與數學上的等号意義不一樣,指派表示一種運算,如i=i+1,表示先計算等号右邊的i+1,再把結果賦給變量i。指派運算符的優先級很低,比到目前為止我們學習過的所有運算符的優先級都低,其結合性是自右向左的,如表達式b=c=d=a+5是一個合法表達式,若a的初值是3,則先計算表達式a+5,結果是8,将8賦給變量d,此時指派表達式“d=a+5”的值是8,再自右向左依次計算c=8,b=8,結果整個表達式的值是8。又如表達式a=5+c=6是非法表達式,因為按照運算符的優先級應先計算5+c,再計算右邊的指派“=”,即将6賦給“5+c”,這是一個非法指派。再如,表達式a=b=5的值是5;表達式a=5+(c=6)的值是11;表達式a=(b=4)+(c=6)的值是10。
2.複合指派運算符
表達式a=a+3可簡寫成a+=3,表達式a=ab可簡寫成a=b。在c++中,所有的二進制算術運算符和二進制位運算符與指派運算符都可以組合成一個複合的指派運算符。它們是:
+= -= *= /= %= <<= >>= &= ^= |=
“複合指派運算符”與“指派運算符”的優先級和結合性是一樣的。如y=x+8,等價于y=(x+8),也等價于y=y(x+8)。又如表達式a+=a-=aa,如果a的初值為2,則先計算a*a,值為4,再計算a-=4,結果a的值變成-2,同時表達式a-=4的值也是-2,再計算a+=(-2),結果a的值是-4,整個表達式的值也是-4。
2.4.9 逗号運算符和逗号表達式
逗号運算符即“,”。逗号表達式格式是:
<表達式1> ,<表達式2>,…,<表達式n>
逗号表達式的求解過程:依次計算<表達式1>,<表達式2>,…,<表達式n>的值。
整個逗号表達式(帶下劃線的)的值為<表達式n>的值。
如逗号表達式a=35,a4,a+5。依次計算表達式a=35、表達式a4、表達式a+5。運算結束後:變量a的值為15,整個表達式的值為20。
逗号運算符的優先級在c++所有的運算符中是最低的,見表2-4,它的結合性是自左向右的。
關于逗号表達式的計算,下面給出幾個例子。請指出下面表達式從最高層面上說是屬于逗号表達式還是屬于指派表達式?運算結束後,給出變量a和x的值,并給出整體表達式的值。
a=35, a4 //是逗号表達式,運算結束後a=15,整體表達式的值為60
x=(a=3, 6*3) //是指派表達式,運算結束後a=3,x=18,整體表達式的值為18
x=a=3, 6*3 //是逗号表達式,運算結束後a=3,x=3,整體表達式的值為18
2.4.10 sizeof 運算符和表達式
sizeof運算符是一進制運算符,它的作用是求一個變量或常量所占用的位元組數。其格式為:
sizeof (<類型辨別>/<變量名>)
例如,已知“int i; double x;”,則sizeof(int)和sizeof(i)均合法,結果均為4;sizeof(double)和sizeof(x)均合法,結果均為8。在2.3.1節中已指出常量的預設類型,是以sizeof(439)的值是4,而sizeof(56.8)的值是8。
2.4.11 邏輯運算優化的副作用
c++語言在計算邏輯表達式的值時,從左向右掃描表達式,表達式的值一旦确定後,就不再繼續進行計算,這就是邏輯運算的優化。具體表現如下所示。
若求<表達式1> && <表達式2>的值,計算時,從左向右掃描,先計算<表達式1>,當<表達式1>為真時,繼續計算<表達式2>;當<表達式1>為假時,即能确定整個表達式的值為假,則停止計算<表達式2>。
若求<表達式1> | | <表達式2>的值,計算時,從左向右掃描,先計算<表達式1>,當<表達式1>為假時,繼續計算<表達式2>;當<表達式1>為真時,即能确定整個表達式的值為真,則停止計算<表達式2 >。
例如:
`int x, y, z, w;
x = y = z = 1 ;
w = ++x || ++y && ++z; // a`
計算結束後,變量x、y、z和w的值分别是2、1、1和1。因為在計算a行表達式時,先計算++x,結果是2,值為真,其後緊跟或運算符 ||,不繼續往右計算了,y和z的值保持不變。指派号右邊邏輯表達式的值為“真”,此時變量w被指派為1。盡管表達式中後一個邏輯與運算符&& 的優先級比邏輯或運算符 || 的優先級高,但&&不會先算,因為對 || 運算符來說,&&運算的結果(即表達式++y && ++z的值)是 || 運算的第二個運算量,第一個運算量的結果已經是“真”了,就不需要計算第二個運算量了。又如
`int x = -1, y = 2, z = 0, w;
w = ++x && ++y || ++z; // b`
計算結束後,變量x、y、z和w的值分别是0、2、1和1。因為在計算b行表達式時,先計算++x,結果是0,值為假,其後緊跟與運算符&&,不繼續計算&&後的運算量++y了,y的值保持不變。但是還要繼續計算++z,因為對于或運算符 || 來說,前面&&運算的結果是 || 運算的第一個運算量,其值為假,需要計算第二個運算量 ++z。