運算子

在介紹了變數和常量後,我們就可以開始使用運算子來對它們進行操作了。下面是運算子的完整列表。目前,可能不需要了解所有運算子,但此處列出所有運算子是為了方便查閱。

賦值運算子(=)

賦值運算子將一個值賦給一個變數。

1
x = 5;

此語句將整數值5賦給變數x。賦值操作始終從右到左進行,絕不會反向操作。

1
x = y;

此語句將變數y中的值賦給變數x。執行此語句時x的當前值將被丟棄,並被y的值替換。

另外請注意,我們只是在賦值操作時將y的值賦給了x。因此,如果y在稍後發生更改,也不會影響x的新值。

例如,讓我們看一下以下程式碼——我在註釋中包含了變數中儲存內容的變化情況。

// assignment operator
#include <iostream>
using namespace std;

int main ()
{
  int a, b;         // a:?,  b:?
  a = 10;           // a:10, b:?
  b = 4;            // a:10, b:4
  a = b;            // a:4,  b:4
  b = 7;            // a:4,  b:7

  cout << "a:";
  cout << a;
  cout << " b:";
  cout << b;
}
a:4 b:7

此程式將在螢幕上列印ab的最終值(分別為4和7)。請注意,即使我們之前聲明瞭a = ba也沒有受到b最終修改的影響。

賦值操作是表示式,可以進行求值。這意味著賦值本身有一個值,並且對於基本型別,該值是在操作中賦的值。例如:

1
y = 2 + (x = 5);

在此表示式中,y被賦值為2與另一個賦值表示式(該表示式本身的值為5)相加的結果。它大致等同於:

1
2
x = 5;
y = 2 + x;

最終結果是將7賦給y

以下表達式在C++中也是有效的:

1
x = y = z = 5;

它將5賦給了所有三個變數:xyz;始終從右到左賦值。

算術運算子(+、-、*、/、%)

C++支援的五種算術運算是:

運算子描述
+加法 (addition)
-subtraction (減法)
*乘法
/除法
%取模

加法、減法、乘法和除法運算分別與其對應的數學運算子字面意義相同。最後一個取模運算子,用百分號(%)表示,給出兩個值相除的餘數。例如:

1
x = 11 % 3;

結果是變數x包含值2,因為11除以3的結果是3,餘數為2。

複合賦值(+=、-=、*=、/=、%=、>>=、<<=、&=、^=、|=)

複合賦值運算子透過對其進行運算來修改變數的當前值。它們等同於將運算結果賦給第一個運算元。

表示式等同於……
y += x;y = y + x;
x -= 5;x = x - 5;
x /= y;x = x / y;
price *= units + 1;price = price * (units+1);

其他複合賦值運算子也是如此。例如:

1
2
3
4
5
6
7
8
9
10
11
// compound assignment operators
#include <iostream>
using namespace std;

int main ()
{
  int a, b=3;
  a = b;
  a+=2;             // equivalent to a=a+2
  cout << a;
}
5

自增自減運算子(++、--)

一些表示式可以縮短:自增運算子(++)和自減運算子(--)將變數中儲存的值增加或減少一。它們分別等同於+=1-=1。因此:

1
2
3
++x;
x+=1;
x=x+1;

在功能上都是等效的;它們三個都將x的值加一。

在早期的C編譯器中,這三個表示式可能會產生不同的可執行程式碼,具體取決於使用哪一個。如今,這種程式碼最佳化通常由編譯器自動執行,因此這三個表示式應該產生完全相同的可執行程式碼。

此運算子的一個特點是它可以作為字首和字尾使用。這意味著它可以寫在變數名前面(++x),也可以寫在變數名後面(x++)。雖然在簡單表示式如x++++x中,兩者含義完全相同;但在其他需要對自增或自減操作的結果進行求值的表示式中,它們的含義可能存在重要差異:如果自增運算子用作值的字首(++x),則表示式的值將是x增加後的最終值。另一方面,如果用作字尾(x++),值也會增加,但表示式的值是x在增加之前的值。請注意區別:

示例 1示例 2
x = 3;
y = ++x;
// x 包含 4,y 包含 4
x = 3;
y = x++;
// x 包含 4,y 包含 3

示例1中,賦給y的值是x增加後的值。而在示例2中,它是x增加之前的值。

關係運算符和比較運算子(==、!=、>、<、>=、<=)

可以使用關係運算符和相等運算子來比較兩個表示式。例如,判斷兩個值是否相等,或者一個值是否大於另一個值。

這種操作的結果是真或假(即布林值)。

C++中的關係運算符是:

運算子描述
==等於
!=不等於
<小於
>大於
<=小於或等於
>=大於或等於

這裡有一些例子:

1
2
3
4
5
(7 == 5)     // evaluates to false
(5 > 4)      // evaluates to true
(3 != 2)     // evaluates to true
(6 >= 6)     // evaluates to true
(5 < 5)      // evaluates to false 

當然,不僅可以比較數字常量,還可以比較任何值,包括變數。假設a=2b=3c=6,則:

1
2
3
4
(a == 5)     // evaluates to false, since a is not equal to 5
(a*b >= c)   // evaluates to true, since (2*3 >= 6) is true
(b+4 > a*c)  // evaluates to false, since (3+4 > 2*6) is false
((b=2) == a) // evaluates to true 

請注意!賦值運算子(=,一個等號)與相等比較運算子(==,兩個等號)不同;第一個(=)將右側的值賦給其左側的變數,而第二個(==)則比較運算子兩側的值是否相等。因此,在最後一個表示式((b=2) == a)中,我們首先將值2賦給了b,然後將其與a(也儲存著值2)進行了比較,結果為true

邏輯運算子(!、&&、||)

!運算子是C++的布林運算NOT運算子。它只有一個運算元,位於其右側,並對其進行取反,如果其運算元為true則產生false,如果其運算元為false則產生true。基本上,它返回其運算元求值結果的相反布林值。例如:

1
2
3
4
!(5 == 5)   // evaluates to false because the expression at its right (5 == 5) is true
!(6 <= 4)   // evaluates to true because (6 <= 4) would be false
!true       // evaluates to false
!false      // evaluates to true 

當對兩個表示式求值以獲得單個關係結果時,使用邏輯運算子&&||&&運算子對應於布林邏輯運算AND,當兩個運算元均為true時為true,否則為false。以下面板顯示了運算子&&求值表示式a&&b的結果:

&& 運算子(AND)
a和 ba && b

||運算子對應於布林邏輯運算OR,當任一運算元為true時為true,僅當兩個運算元都為false時才為false。以下是a||b的可能結果:

|| 運算子(OR)
a和 ba || b

例如:
1
2
( (5 == 5) && (3 > 6) )  // evaluates to false ( true && false )
( (5 == 5) || (3 > 6) )  // evaluates to true ( true || false ) 

在使用邏輯運算子時,C++從左到右僅評估必要的部分以得出組合關係結果,忽略其餘部分。因此,在最後一個示例((5==5)||(3>6))中,C++首先評估5==5是否為true,如果是,則從不檢查3>6是否為true。這被稱為短路求值,對於這些運算子的工作方式如下:

運算子短路
&&如果左側表示式為false,則組合結果為false(右側表示式永遠不求值)。
||如果左側表示式為true,則組合結果為true(右側表示式永遠不求值)。

當右側表示式具有副作用時,例如修改值,這一點尤其重要。

1
if ( (i<10) && (++i<n) ) { /*...*/ }   // note that the condition increments i 

在這裡,組合條件表示式會將i加一,但這僅在&&左側的條件為true時發生,因為否則,右側的條件(++i<n)永遠不會被求值。

條件三元運算子(?)

條件運算子對一個表示式進行求值,如果該表示式求值為true,則返回一個值,如果表示式求值為false,則返回另一個值。其語法是:

condition ? result1 : result2

如果conditiontrue,則整個表示式求值為result1,否則為result2

1
2
3
4
7==5 ? 4 : 3     // evaluates to 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3   // evaluates to 4, since 7 is equal to 5+2.
5>3 ? a : b      // evaluates to the value of a, since 5 is greater than 3.
a>b ? a : b      // evaluates to whichever is greater, a or b.  

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// conditional operator
#include <iostream>
using namespace std;

int main ()
{
  int a,b,c;

  a=2;
  b=7;
  c = (a>b) ? a : b;

  cout << c << '\n';
}
7

在此示例中,a為2,b為7,因此被求值的表示式(a>b)不為true,因此將問題標記後的第一個值(冒號後的值)b(值為7)所替換。

逗號運算子(,)

逗號運算子(,)用於分隔兩個或多個表示式,這些表示式包含在只能有一個表示式的地方。當需要對一組表示式進行求值以獲得一個值時,只考慮最右側的表示式。

例如,以下程式碼
1
a = (b=3, b+2);

將首先把值3賦給b,然後把b+2賦給變數a。所以,最後,變數a將包含值5,而變數b將包含值3。

位運算子(&、|、^、~、<<、>>)

位運算子透過考慮表示其儲存值的位模式來修改變數。

運算子彙編等效描述
&與 (AND)按位與
|或 (OR)按位包含或
^異或按位異或
~非 (NOT)一元補碼(位反轉)
<<SHL左移位
>>SHR右移位

顯式型別轉換運算子

型別轉換運算子允許將一種型別的值轉換為另一種型別。在C++中有多種方法可以做到這一點。最簡單的一種,是從C語言繼承而來的,是使用括號(())括起來的新型別作為要轉換的表示式的字首。

1
2
3
int i;
float f = 3.14;
i = (int) f;

前面的程式碼將浮點數3.14轉換為整數值(3);小數部分被丟棄。這裡,型別轉換運算子是(int)。在C++中執行相同操作的另一種方法是使用函式式表示法,將要轉換的型別作為表示式的字首,並將表示式括在括號中。

1
i = int (f);

這兩種型別轉換方法在C++中都是有效的。

sizeof

此運算子接受一個引數,該引數可以是型別或變數,並返回該型別或物件的位元組大小。

1
x = sizeof (char);

這裡,x被賦為值1,因為char是一種大小為一位元組的型別。

sizeof返回的值是一個編譯時常量,因此它總是在程式執行之前確定的。

其他運算子

在後續的教程中,我們將看到更多運算子,例如指向指標的運算子或面向物件程式設計的特定運算子。

運算子優先順序

一個表示式可能包含多個運算子。例如:

1
x = 5 + 7 % 2;

在C++中,上面的表示式總是將6賦給變數x,因為%運算子的優先順序高於+運算子,並且總是先進行計算。表示式的部分可以透過括號括起來來覆蓋此優先順序順序,或使其意圖明確。請注意區別:

1
2
x = 5 + (7 % 2);    // x = 6 (same as without parenthesis)
x = (5 + 7) % 2;    // x = 0 

從最高到最低優先順序,C++運算子的計算順序如下:
級別優先順序組運算子描述分組
1作用域::作用域限定符從左到右
2字尾(一元)++ --字尾自增/自減從左到右
()函式形式
[]下標
. ->成員訪問
3字首(一元)++ --字首自增/自減從右到左
~ !按位非/邏輯非
+ -一元字首
& *引用/解引用
new delete分配/釋放
sizeof引數包
(型別)C風格型別轉換
4指標到成員.* ->*訪問指標從左到右
5算術:縮放* / %乘法、除法、取模從左到右
6算術:加法+ -加法、減法從左到右
7位移<< >>左移、右移從左到右
8關係< > <= >=比較運算子從左到右
9相等== !=相等/不等從左到右
10&按位與從左到右
11異或^按位異或從左到右
12|按位或從左到右
13合取&&邏輯與從左到右
14析取||邏輯或從左到右
15賦值級表示式= *= /= %= += -=
>>= <<= &= ^= |=
賦值/複合賦值從右到左
?:條件運算子
16排序,逗號分隔符從左到右

當表示式有兩個相同優先順序的運算子時,分組決定了哪個運算子先被求值:是從左到右還是從右到左。

將所有子語句括在括號中(即使是由於優先順序而不需要的)可以提高程式碼的可讀性。
Index
目錄