## 1.4 操作符/運算符(Operators)
前面已經學習了變量和常量,我們可以開始對它們進行操作,這就要用到C++的操作符。有些語言,很多操作符都是一些關鍵字, 比如add, equals等等。C++的操作符主要是由符號組成的。這些符號不在字母表中,但是在所有鍵盤上都可以找到。這個特點使得C++程序更簡潔,也更國際化。運算符是C++語言的基礎,所以非常重要。
你不需要背下所有這一小節的內容,這些細節知識僅供你以后需要時參考 。
### 賦值Assignation (=)
賦值運算符的功能是將一個值賦給一個變量。
`a = 5;`
將整數5賦給變量a。= 運算符左邊的部分叫做lvalue (left value),右邊的部分叫做rvalue (right value)。lvalue 必須是一個變量,而右邊的部分可以是一個常量,一個變量,一個運算(operation)的結果或是前面幾項的任意組合。
有必要強調賦值運算符永遠是將右邊的值賦給左邊,永遠不會反過來。
`a = b;`
將變量b (rvalue)的值賦給變量a (lvalue),不論a當時存儲的是什么值。同時考慮到我們只是將b的數值賦給a,以后如果b的值改變了并不會影響到a的值.
例如:如果我們使用以下代碼(變量值的變化顯示在綠色注釋部分):
~~~
// 賦值符號例子
#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;
return 0;
}
~~~
~~~
a:4 b:7
~~~
以上代碼結果是a的值為4, b的值為7。最后一行中b的值被改變并不會影響到a,雖然在此之前我們聲明了a = b; (從右到左規則right-to-left rule)。
C++擁有而其他語言沒有的一個特性是賦值符 (=) 可以被用作另一個賦值符的rvalue (或rvalue的一部分) 。例如:
`a = 2 + (b = 5);`
等同于:
`b = 5;
a = 2 + b;`
它的意思是:先將5賦給變量b,然后把前面對b的賦值運算的結果(即5)加上2再賦給變量a,這樣最后a中的值為7。因此,下面的表達式在C++中也是正確的:
a = b = c = 5; //將5同時賦給3個變量a, b和c。
### 數學運算符Arithmetic operators ( +, -, *, /, % )
C++語言支持的5種數學運算符為:
* `+` 加addition
* `-` 減subtraction
* `*` 乘multiplication
* `/` 除division
* `%` 取模module
加減乘除運算想必大家都很了解,它們和一般的數學運算符沒有區別。
唯一你可能不太熟悉的是用百分號(%)表示的取模運算(module)。取模運算是取兩個整數相除的余數。例如,如果我們寫a = 11 % 3;,變量a的值將會為結果2,因為2是11除以3的余數。
### 組合運算符Compound assignation operators (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)
C++以書寫簡練著稱的一大特色就是這些組合運算符compound assignation operators (+=, -=, *= 和 /= 及其他) ,這些運算符使得只用一個基本運算符就可改寫變量的值:
~~~
value += increase; 等同于 value = value + increase;
a -= 5; 等同于 a = a - 5;
a /= b; 等同于 a = a / b;
price *= units + 1; 等同于 price = price * (units + 1);
~~~
其他運算符以此類推。例如:
~~~
// 組合運算符例子
#include <iostream>
using namespace std;
int main ()
{
int a, b=3;
a = b;
a+=2; // 相當于 a=a+2
cout << a;
return 0;
}
~~~
~~~
5
~~~
### 遞增和遞減Increase and decrease
書寫簡練的另一個例子是遞增(increase)運算符 (++)和遞減(decrease) 運算符(--)。它們使得變量中存儲的值加1或減1。它們分別等同于+=1和-=1。因此:
`a++;
a+=1;
a=a+1;
`
在功能上全部等同,即全部使得變量a的值加1。
它的存在是因為最早的C編譯器將以上三種表達式的編譯成不同的機器代碼,不同的機器代碼運行速度不一樣。現在,編譯器已經基本自動實行代碼優化,所以以上三種不同的表達方式編譯成的機器代碼在實際運行上已基本相同。
這個運算符的一個特點是它既可以被用作prefix前綴,也可以被用作后綴suffix,也就是說它既可以被寫在變量標識的前面(++a),也可以被寫在后面(a++)。雖然在簡單表達式如a++或++a中,這兩種寫法代表同樣的意思,但當遞增increase或遞減decrease的運算結果被直接用在其它的運算式中時,它們就代表非常不同的意思了:當遞增運算符被用作前綴prefix (++a) 時,變量a的值線增加,然后再計算整個表達式的值,因此增加后的值被用在了表達式的計算中;當它被用作后綴suffix (a++)時,變量a的值在表達式計算后才增加,因此a在增加前所存儲的值被用在了表達式的計算中。注意以下兩個例子的不同:
| 例 1 | 例 2 |
|---|---|
| B=3;<br/>A=++B;<br/>// A 的值為 4, B 的值為 4 | B=3;<br/>A=B++;<br/>// A 的值為 3, B 的值為 4 |
在第一個例子中,**B**在它的值被賦給**A**之前增加1。而在第二個例子中**B**原來的值3被賦給 **A**然后**B**的值才加1變為4。
### 關系運算符Relational operators ( ==, !=, >, =, <= )
我們用關系運算符來比較兩個表達式。如ANSI-C++ 標準中指出的,關系預算的結果是一個bool值,根據運算結果的不同,它的值只能是真**true**或**false**。
例如我們想通過比較兩個表達式來看它們是否相等或一個值是否比另一個的值大。以下為C++的關系運算符:
|||
|---|---|
| == | 相等Equal |
| != | 不等Different |
| > | 大于Greater than |
| < | 小于Less than |
| >= | 大于等于Greater or equal than |
| <= | 小于等于Less or equal than |
下面你可以看到一些實際的例子:
|||
|---|---|
| (7 == 5) | 將返回false. |
| (5 > 4) | 將返回true. |
| (3 != 2) | 將返回true. |
| (6 >= 6) | 將返回true. |
| (5 < 5) | 將返回false. |
當然,除了使用數字常量,我們也可以使用任何有效表達式,包括變量。假設有a=2, b=3和c=6,
|||
|---|---|
| (a == 5) | 將返回false. |
| (a*b >= c) | 將返回true 因為它實際是(2*3 >= 6) |
| (b+4 > a*c) | 將返回false因為它實際是(3+4 > 2*6) |
| ((b=2) == a) | 將返回true. |
**注意:**運算符**=** (單個等號)不同于運算符**==** (雙等號)。第一個是賦值運算符(將等號右邊的表達式值賦給左邊的變量);第二個(==)是一個判斷等于的關系運算符,用來判斷運算符兩邊的表達式是否相等。因此在上面例子中最后一個表達式((b=2) == a),我們首先將數值2賦給變量b,然后把它和變量a進行比較。因為變量a中存儲的也是數值2,所以整個運算的結果為true。
在ANSI-C++標準出現之前的許多編譯器中,就像C語言中,關系運算并不返回值為真**true**或假**false**的**bool**值,而是返回一個整型數值最為結果,它的數值可以為**0**,代表"**false**"或一個非**0**數值(通常為**1**)來代表"**true**"。
### 邏輯運算符Logic operators ( !, &&, || )
運算符 ! 等同于**boolean** 運算**NOT** (取非),它只有一個操作數(operand),寫在它的右邊。它做的唯一工作就是取該操作數的反面值,也就是說如果操作數值為真**true**,那么運算后值變為假**false**,如果操作數值為假**false**,則運算結果為真**true**。它就好像是說取與操作數相反的值。例如:
|||
|---|---|
| !(5 == 5) | 返回false,因為它右邊的表達式(5 == 5)為真true. |
| !(6 <= 4) | 返回true因為(6 <= 4)為假false. |
| !true | 返回假false. |
| !false | 返回真true. |
邏輯運算符&&和**||**是用來計算兩個表達式而獲得一個結果值。它們分別對應邏輯運算中的與運算**AND** 和或運算**OR**。它們的運算結果取決于兩個操作數(operand)的關系:
|||
|---|---|---|---|
| 第一個操作數a | 第二個操作數b | 結果 a && b | 結果a \| \| b |
| true | true | true | true |
| true | false | false | true |
| false | true | false | true |
| false | false | false | false |
例如 :
~~~
( (5 == 5) && (3 > 6) ) 返回false ( true && false ).
( (5 == 5) || (3 > 6)) 返回true ( true || false ).
~~~
### 條件運算符Conditional operator ( ? )
條件運算符計算一個表達式的值并根據表達式的計算結果為真true或假false而返回不同值。它的格式是:
condition ? result1 : result2 (條件?返回值1:返回值2)
如果條件condition 為真true,整個表達式將返回esult1,否則將返回result2。
|||
|---|---|
| 7==5 ? 4 : 3 | 返回3,因為7不等于5. |
| 7==5+2 ? 4 : 3 | 返回4,因為7等于5+2. |
| 5>3 ? a : b | 返回a,因為5大于3. |
| a>b ? a : b | 返回較大值,a 或b. |
~~~
// 條件運算符例子
#include <iostream>
using namespace std;
int main ()
{
int a,b,c;
a=2;
b=7;
c = (a>b) ? a : b;
cout << c;
return 0;
}
~~~
~~~
7
~~~
上面的例子中a的值為2,b的值為7,所以表達式(a>b)運算值為假(false),所以整個表達式(a>b)?a:b要取分號后面的值,也就是b的值7。因此最后輸出 c 的值為7。
### 逗號運算符 ( , )
逗號運算符 (,) 用來分開多個表達式,并只取最右邊的表達式的值返回。
例如有以下代碼:
~~~
a = (b=3, b+2);
~~~
這行代碼首先將3賦值給變量b,然后將 b+2 賦值給變量 a。所以最后變量a 的值為5,而變量b的值為3。
### 位運算符Bitwise Operators ( &, |, ^, ~, > )
位運算符以比特位改寫變量存儲的數值,也就是改寫變量值的二進制表示:
||||
|---|---|---|
| op | asm | Description |
| & | AND | 邏輯與 Logic AND |
| \| | OR | 邏輯或Logic OR |
| ^ | XOR | 邏輯異或Logical exclusive OR |
| ~ | NOT | 對1取補(位反轉)Complement to one (bit inversion) |
| << | SHL | 左移Shift Left |
| >> | SHR | 右移Shift Right |
### 變量類型轉換運算符Explicit type casting operators
變量類型轉換運算符可以將一種類型的數據轉換為另一種類型的數據。在寫C++中有幾種方法可以實現這種操作,最常用的一種,也是與C兼容的一種,是在原轉換的表達式前面加用括號()括起的新數據類型:
~~~
int i;
float f = 3.14;
i = (int) f;
~~~
以上代碼將浮點型數字**3.14**轉換成一個整數值(**3**)。這里類型轉換操作符為(**int**)。在C++中實現這一操作的另一種方法是使用構造函數constructor 的形式:在要轉換的表達式前加變量類型并將表達式括在括號中:
~~~
i = int ( f );
~~~
以上兩種類型轉換的方法在C++中都是合法的。另外ANSI-C++針對面向對象編程(object oriented programming)增加了新的類型轉換操作符 (參考[Section 5.4, Advanced class type-casting](http://www.prglab.com/cms/pages/c-tutorial/advanced-concepts/class-type-casting.php)).
### sizeof()
這個運算符接受一個輸入參數,該參數可以是一個變量類型或一個變量自己,返回該變量類型(variable type) 或對象(object)所占的字節數:
~~~
a = sizeof (char);
~~~
這將會返回1給a,因為char是一個常為1個字節的變量類型。
sizeof返回的值是一個常數,因此它總是在程序執行前就被固定了。
### 其它運算符
在本教程后面的章節里我們將看到更多的運算符,比如指向指針的運算或面向對象編程特有的運算,等等,我們會在它們各自的章節里進行詳細討論。
### 運算符的優先度 Precedence of operators
當多個操作數組成復雜的表達式時,我們可能會疑惑哪個運算先被計算,哪個后被計算。例如以下表達式:
`a = 5 + 7 % 2`
我們可以懷疑它實際上表示:
a = 5 + (7 % 2) 結果為**6**,還是 a = (5 + 7) % 2 結果為**0**?
正確答案為第一個,結果為6。每一個運算符有一個固定的優先級,不僅對數學運算符(我們可能在學習數學的時候已經很了解它們的優先順序了),所有在C++中出現的運算符都有優先級。從最從最高級到最低級,運算的優先級按下表排列:
<table >
<tbody><tr><th>優先級<br>Level</th><th>操作符<br>Operator</th><th>說明<br>Description</th><th>結合方向<br>Grouping</th></tr>
<tr><td>1</td><td><tt>::</tt></td><td>范圍</td><td>從左到右</td></tr>
<tr><td>2</td><td><tt>() [] . -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid</tt></td><td>后綴</td><td>從左到右</td></tr>
<tr><td rowspan="3">3</td><td><tt>++ -- ~ ! sizeof new delete</tt></td><td>一元(前綴)</td><td rowspan="3">從右到左</td></tr>
<tr><td><tt>* &</tt></td><td>指針和取地址 <br></td></tr>
<tr><td><tt>+ -</tt></td><td>一元符號</td></tr>
<tr><td>4</td><td><tt>(type)</tt></td><td>類型轉換 <br></td><td>從右到左</td></tr>
<tr><td>5</td><td><tt>.* ->*</tt></td><td>指向成員的指針<br></td><td>從左到右</td></tr>
<tr><td>6</td><td><tt>* / %</tt></td><td>乘、除、取模 <br></td><td>從左到右</td></tr>
<tr><td>7</td><td><tt>+ -</tt></td><td>加減</td><td>從左到右</td></tr>
<tr><td>8</td><td><tt><< >></tt></td><td>位移<br></td><td>從左到右</td></tr>
<tr><td>9</td><td><tt>< > <= >=</tt></td><td>關系操作符</td><td>從左到右</td></tr>
<tr><td>10</td><td><tt>== !=</tt></td><td>等于、不等于<br></td><td>從左到右</td></tr>
<tr><td>11</td><td><tt>&</tt></td><td>按位與運算<br></td><td>從左到右</td></tr>
<tr><td>12</td><td><tt>^</tt></td><td>按位異或運算</td><td>從左到右</td></tr>
<tr><td>13</td><td><tt>|</tt></td><td>按位或運算</td><td>從左到右</td></tr>
<tr><td>14</td><td><tt>&&</tt></td><td>邏輯與運算</td><td>從左到右</td></tr>
<tr><td>15</td><td><tt>||</tt></td><td>邏輯或運算</td><td>從左到右</td></tr>
<tr><td>16</td><td><tt>?:</tt></td><td>條件運算</td><td>從右到左</td></tr>
<tr><td>17</td><td><tt>= *= /= %= += -= >>= <<= &= ^= |=</tt></td><td>賦值運算</td><td>從右到左</td></tr>
<tr><td>18</td><td><tt>,</tt></td><td>逗號</td><td>從左到右</td></tr>
</tbody></table>
結合方向Grouping定義了當有同優先級的多個運算符在一起時,哪一個必須被首先運算,最右邊的還是最左邊的。
所有這些運算符的優先級順序可以通過使用括號parenthesis signs (和)來控制,而且更易讀懂,例如以下例子:
`a = 5 + 7 % 2;`
根據我們想要實現的計算的不同,可以寫成:
`a = 5 + (7 % 2); 或者
a = (5 + 7) % 2;`
所以如果你想寫一個復雜的表達式而不敢肯定各個運算的執行順序,那么就加上括號。這樣還可以使代碼更易讀懂。