表達式(expression):對各種物件(數值)進行運算的過程。
運算後就會產生結果。故表達式所表達的就是一個結果。
例如:
3 + 2 就是一個表達式。其結果為
5。
"Hello World"
此為『字串常數表達式』,此表達式的結果為字串物件(也是最簡單表達式的一種)。
參與運算的物件(數值)被稱為『運算元(operand)』,又稱『運算對象』。
3 + 2 的運算元就是 3 與
2。進行運算操作的符號則為『運算子(operator)』,又稱『運算符』。
3 + 2 的運算子就是 + 。一般情況,表達式會有一個或多個運算子。
若表達式的運算元也是一個表達式,則稱為『複合表達式』。
1.2 * (basicSalary + bonus - penalty) // 計算薪水的表達式子表達式的計算順序是由運算子的優先級(precedence)與結合律(associativity)共同決定。
若優先級相同,則由結合律來決定。以四則運算為例:
例如乘法與除法優先級相同,乘法與除法的運算元會優先進行運算,接著結果才能為加法或減法的運算元。
而算術運算子滿足『左結合律』,意指當運算子優先級相同時,將按照由左至右的順率組合運算元。
表達式中的括號(())可無視上述規則,程式設計師可使用括號將表達式的某個部分括起來使其得到優先運算。
表達式的結果不一定有 ;。
若帶有
;,則成為一個『語句(statement)』。
一元運算子:作用於一個運算元的運算子,如取址運算子(&)與解參考運算字(*)。
二元運算子:作用於兩個運算元的運算子,如相等運算子(==)與乘法運算子(*)。
三元運算子:C++中只有一個三元運算子:條件運算子
? :。
函數呼叫運算子:()。
運算子有時可做為一元運算子,也可能做為二元運算子,如
*。端看該段程式碼上下文來決定。
也可以按照運算子操作結果,可分為:
算術運算子
邏輯與關係運算子
賦值運算子:=
條件運算子:? :
sizeof 運算子
位元運算子
C++的表達式(回傳結果)為『左值(lvalue)』或『右值(rvalue)』,二選一。
初級(一般)定義:
左值:可放在賦值語句『左側』:代表一個『位址』,這個位址可以存放『值』。
右值:無法放在賦值語句左側,就是『右值』。
進階定義:
當物件被作為『右值』使用時,用的是它的內容(值)。
當物件被作為『左值』使用時,用的是它在記憶體的位址。
故一個物件可依不同表達式(使用情境),有時為左值,有時為右值。
需要右值的地方可用左值來替代。
但無法把右值當做左值使用。
記住:不同運算子對運算元的要求各不相同:
有時會需要左值的運算元,有時需要右值的運算元。
在需要右值的地方可以用左值代替,但無法將右值當作左值使用。
回傳值則會有『回傳左值』或『回傳右值』的差別。
當左值被當成右值使用時,實際使用的是它的內容。
賦值運算子(=)需要一個左值作為其『左側』運算元,得到結果也是一個『左值』。
取址運算子(&)作用於一個『左值』運算元,並回傳一個指向該運算元的指標,此指標為『右值』。
解參考運算子(*)、下標運算子([])、迭代器解參考運算子(*iterator)的求值結果都是『左值』。
遞增(++)遞減(–-)運算子作用於『左值』運算元,而前置版本回傳結果為『左值』,後置版本回傳結果為『右值』。
decltype
括弧內的表達式求值結果(非變數)若為『左值』時,decltype
推論的型態為『參考』型態。
在C++中,decltype
的功能是用於取得表達式的型態。
若 p 的型態為指向 int 型態的指標變數(
int*),則因對 p 進行解參考而得到『左值』:
decltype(*p) // 推論出 int&
decltype(&p) // 推論出 int**,指向整數指標的指標回傳『非參考』型態的函數(算術、關係、後置遞增運算子)都將回傳『右值』。故無法將(左值)參考綁定這種類型的表達式上,但可將 『const 左值參考』或『右值參考』將其做綁定。
『臨時物件』就是一個右值。
在表達式求值過程中,運算元可能會發生由一種型態轉換為另一種型態。
例如:一般二元運算子都要求兩個運算元的型態相同,但很多時候運算元型態不同也可計算,只要他們能被轉換為相同型態即可。
int ival = 3.541 + 3; // 編譯器可能會警告該運算會損失精度
加法的兩個運算元型態不同:3.541為 double,3 為
int。
3 會轉為 double 型態。
上述程式會自動發生型態轉換,故又稱『隱式轉換(implicit conversion)』
隱式轉換的原則是盡量避免精度損失。
不同型態進行混合運算時,系統會嘗試將運算元的型態統一,會選取參與運算的運算元所能表達最大範圍的型態作為其他運算元的目標型態,例如:
int + double = double
char + int = int
char + short = int
float + float = double(float)
在條件判斷中,非布林值會被轉換成布林值。
初始化過程中,初始值會被轉換為變數的型態。
注意:若運算元為『unsigned』型態,另外一個運算元為『signed』型態,則『signed』型態會被轉換為『unsigned』型態:
unsigned + signed = unsigned
故當某一個 int
的值為『負值』,則可能會發生副作用。
故建議運算時盡量保持型態一致,並確保運算結果不要發生溢出現象。
『溢出(overflow)』定義:數據值超出其資料型態所能表達的範圍。
當發生溢出時,會發生資料遺失、程式錯誤、安全性或不確定行為的負面影響。
float a = 2.2f;
float b = 3.5f;
auto result1 = a + 12.4; // double
auto result2 = a + b;
char ac = 2, bc = 3;
auto result3 = ac + bc; // int
short as = 1, as2 = 6;
auto result4 = as + as2; // int
long aaa = 15;
float bbb = 16.7f;
auto result5 = aaa + bbb; // float| 算術運算子(左結合律) | 功能 | 語法 |
|---|---|---|
|
一元正號 一元負號 |
|
|
乘法 除法 餘數除法 |
|
|
加法 減法 |
|
一元運算子優先級最高。接下是乘法、除法與餘數除法,優先級最低是加法與減法。
% 餘數除法要求兩側都須為『整數型態』,如
7 % 4 = 3。
int ival = 42;
double dval = 3.14;
ival % 12; // 正確,結果為 6。
ival % dval; // 錯誤。運算元是 double 型態。一元正(負)號運算子對運算元取正(負)號後,其回傳的是『副本』。
bool b = true;
bool b2 = -b; // b2 也是 true!。
std::cout << b2 << std::endl;
std::cout << std::boolalpha << b2 << std::endl; // std::boolalpha 是 I/O 運算子,可使 std::cout 將 true 和 false 打印出字串而不是整数。
不應該對布林值進行運算。上述 -b
就是一個典型的例子。
對大多數運算子而言,布林型態的運算元將會被提升至 int
型態。故上述例子,參與運算後將被提升為整數值 1 ,對他取負號後為
-1。接著再將其轉換為布林型態,並作為 b2 的初始值,而此初始值並非為
0。故轉換為布林值後其值為 1,故 b2 值為 true。
| 結合律 | 運算子 | 功能 | 語法 |
|---|---|---|---|
| 右 | ! |
邏輯 非 | !expr |
左 左 左 左 |
|
(關係運算子) 小於 小於等於 大於 大於等於 |
|
左 左 |
|
相等 不相等 |
|
| 左 | && |
邏輯且 | expr && expr |
| 左 | || |
邏輯 或 | expr || expr |
『邏輯且』運算子(&&):當左右兩側運算元都為真時,結果為真。
『邏輯或』運算子(||):只要當兩側運算元中一個為真時,結果為真。
&& 與
||運算子都是由左側運算元先運算,再求右側運算元之值。
短路求值(short-circuit evalution):只有當左側運算元無法確認表達式最後結果時,才會對右側運算元進行求值。
&&:左側為真(true),才會對右側運算元求值。
||:左側為假(false),才會對右側運算元求值。
『邏輯非』(!)運算子會將運算元之值取相反後並將其回傳之。
關係運算子:比較兩側運算元大小關係並回傳布林值。滿足『左結合律』。注意下例:
if (i < j < k) // 注意:拿 i < j 的布林值結果與k比較
{ // 若k大於1則一定為真
}
if (i < j && j < k) // 應該寫成這樣才正確。
{
}相等性測試與布林字面值
if (val) {/*......*/ } // 若val是任意非0值,則條件為真
if (!val) {/*.....*/} // 若val為0,則條件為真。
if (val == true) {/*.....*/} // 只有當val等於1時,條件才為真。
若 val 不是布林值,則進行比較前會先將 true 轉換為 val 的型態,意指:
if (val == 1) {/*.....*/}進行比較運算時,除非比較的對象為布林值,建議不要使用布林字面值
true 或 false 作為運算元。
賦值運算子(=)左側的運算元一定是一個可被修改的『左值(變數)』,並將『等號右側』的值賦值給『等號左側』的變數。
可理解成:給變數一個值,或是更改變數內容為某一個值。
char a;:此為變數宣告(未初始化)。系統會將變數
a 分配一個 byte的記憶體空間(未定義值)。
char a = 90;:定義時初始化(非賦值語句)。系統會給變數
a 分配記憶體空間,接著在這個內存上放入值 90。
int i = 0, j = 0, k = 0 // 初始化而非賦值。
const int ci = i; // 初始化而非賦值。
下列賦值語句皆為錯誤:
1024 = k; // 錯誤:字面值為右值。
i + k = k; // 錯誤:算術表達式為右值。
ci = k; // 錯誤:ci為 const 常數的左值。賦值表達式的結果是『左側運算元』,且其為『左值』,而結果的型態就是左側運算元的型態。
若賦值運算子的左右兩側運算元型態不同,則右側運算元將轉換成左側運算元的型態。
k = 0; // 型態為int,值為0。
k = 3.14; // 型態為int,值為3。C++ 11
標準:允許使用大括弧({})的『初始化列表』作為賦值運算子右側的運算元:
k = {3.14}; // 錯誤:發生型態窄化轉換。
std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 賦值運算子滿足『右結合律』,此與二元運算子不同。
int ival, jval;
ival = jval = 0; // 正確:都被賦值為0。
// ival = (jval = 0); // 滿足右結合律。
int ival, *pval;
ival = pval = 0; // 無法將指標值(int*)賦值給int賦值運算子(=)優先級較低。
int i;
while ( (i = get_value() ) != 42)
{
// 其他的處理
}
上述程式碼 while 的條件內若不加上
(),則比較運算子 != 的運算元將是
get_value 函數回傳值與
42,比較結果顯然不是我們所預期的答案。
故在條件語句中,賦值部分應該加上()。
複合賦值運算子。
a = a operator b
+=
-=
*=
/=
%=
優先級順序:賦值運算子 < 關係運算子 < 算術運算子。
共兩種:
遞增運算子(++):為物件加一,又分『前置』,如
++i 與『後置』,如 i++。
遞減運算子(--):為物件減一,又分『前置』,如
--i 與『後置』,如 i--。
前置遞增(遞減)運算子:對『左值運算元』加一(減一),再將『改變之後的物件』作為執行結果(作為回傳值)。
後置遞增(遞減)運算子:也是對『左值運算元』加一(減一),但執行結果是運算元被『改變之前的物件』版本的拷貝。
int i = 0, j;
j = ++i;
std::cout << "j = ++i" << std::endl;
std::cout << "i = "<< i << std::endl;
std::cout << "j = "<< j << std::endl;
int i = 0, j;
j = i++;
std::cout << "ex: i++" << std::endl;
std::cout << "j = i++" << std::endl;
std::cout << "i = "<< i << std::endl;
std::cout << "j = "<< j << std::endl;前置版本:將物件本身作為『左值』回傳。
後置版本:將物件的原始值的副本(拷貝)作為『右值』回傳。
注意:除非必要,建議『不要』使用遞增遞減運算子的『後置版本』,原因如下:
前置版本避免了不必要的工作:它將運算元加一(減一)後,直接回傳改變後的運算元。
後置版本需要將原始值進行儲存,以便於之後回傳未修改的內容。
若不需要修改前的原始值,則建議使用前置版本。
範例——語句中混用『解參考運算子』與『遞增運算子』:
auto pbeg = v.begin();
// 輸出元素直到遇到第一個負值為止
while (pbeg != v.end() && *beg >= 0)
{
std::cout << *pbeg++ << std::endl; // 輸出當前值,並將pbeg向前移動一個元素
}
// std::cout << *iter++ << std::endl; // 更簡潔~~
// 上述語句可以取代下列兩行語句:
// std::cout << *iter << std::endl;
// ++iter;
*pbeg++ 等於
*(pbeg++):因為『遞增運算子』的優先級高於『解參考運算子』,故
() 可以省略。
此時解參考運算子的運算元是『pbeg』未增加之前的值。
此語句輸出 pbeg
開始時指向的那個元素,並將指標向前移動一個位置。
注意:大部分運算子沒有規定運算元的求值順序。故當一條子表達式改變運算元的值,而存在另外一條使用該運算元值的子表達式時,求值順序就非常重要了。
for (auto it = s.begin(); it != s.end() && !issapce(*it); ++i)
{
*it = toupper(*it); // 將當前字元改為大寫形式。
}
while (beg != s.end() && !isspace(*beg))
{
*beg = toupper(*beg++); // 錯誤:該賦值語句無法定義
}
上述範例將產生未定義的原因為:
賦值運算子左右兩側的運算元都用到
beg,且右側的運算元還改變了 beg
的值,故該語句是未定義的。
編譯器可能按照下列任意一種方式處理該表達式:
*beg = toupper(*beg); // 若先求左側的值。
*(beg + 1) = toupper(*beg); // 若先求右側的值。
前置遞增遞減運算子(++i 與
–-i)為『左值表達式』(回傳左值):
++i:直接給 i 變數加 1,然後回傳 i 本身。因為 i
為變數,所以可以被賦值,故為左值表達式。
int i = 5;
(++i) = 20; // i被賦值為20。後置遞增遞減運算子(i++ 與
i--)為『右值表達式』(回傳右值):
i++:先產生一個『臨時變數』來保存 i
的值用於回傳,接著再對 i 加
1,之後系統在釋放上述的『臨時變數』。被釋放掉的臨時變數將無法被賦值。故為『右值表達式』。
int i = 5;
// (i++) = 20; // 錯誤:表達式必須是可被修改的左值。int i = 1;
int &&r1 = i++; // 正確:右值參考可以綁定右值,但與r1與i沒有關係。
// int &r2 = i++; // 錯誤:左值參考無法綁定右值表達式。
int &r3 = ++i; // 正確:左值參考可以綁定左值。
// int &&r4 = ++i; // 錯誤:右值參考無法綁定左值。
std::cout << "i = " << i << std::endl;
std::cout << "r1 = " << r1 << std::endl;
std::cout << "r3 = " << r3 << std::endl;
補充說明:
&&r1綁定右值,但 r1
本身為左值。
所有變數都為左值。
任何函數的形式參數都為左值,如
void f(int &&w),形式參數 w
的型態為右值參考(意指其可以綁定右值),但 w
本身為左值。
點運算子(.):可取得物件的成員(member)。
箭頭運算子(->):等價於
(*ptr).mem。
std::string s1 = "a string", *p = &s1;
auto n = s1.size(); // 執行string物件s1的size成員。
n = (*p).size(); // 執行p所指向的物件的size成員。
n = p->size(); // 等價於 (*p).size()。因為『解參考運算子』的優先級低於『點運算子』,故執行解參考運算的子表達式必須加上
()。
// *p.size(); // 錯誤:p為一個指標,它並沒有名為size的成員。箭頭運算子作用於一個指標型態的運算元,其結果為『左值』。
點運算子有兩種情況:
若成員所屬的物件為左值,則結果為左值。
若成員所屬的物件為右值,則結果為右值。
條件運算子(? :):允許將簡單的 if-else
的邏輯嵌入到單個表達式中:
語法:cond ? expr1 : expr2;
其中,cond 是判斷條件的表達式,而 expr1
與 expr2
是兩個型態相同或可能轉換為某個型態的表達式。
先執行 cond
的值,若條件為真(true)則對 expr1
求值並回傳該值,否則對 expr2 求值並回傳結果。
max = (a > b)? a:b;逗點運算子( , comma
operator)是優先級最低的運算子。
逗點運算子含兩個運算元,按照由左至右的順序依序求值:
表達式1, 表達式2, 表達式3, ……., 表達式N
逗點運算子先對左側表達式求值,接著將求值結果丟棄。其真正的回傳值為『最右側表達式』之值(表達式N)。
std::vector<int>::size_type cnt = ivec.size();
for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix, --cnt)
ivec[ix] = cnt;
上述循環 for 語句中的表達式進行:遞增
ix,遞減 cnt,每次循環迭代 ix 與
cnt 都會改變。
只要 ix 滿足條件,則將當前元素設定為
cnt 當下之值。
int a;
a = (4, 5); // a = 5,因為逗點運子優先級太低,故必須加()。
a = (3 + 5, 6 + 8); // a = 14。
a = 3 * 5, a * 4; // 表達式值為60。因為先計算a = 3*5,再計算a*4。故a的值為15,整個逗點表達式為60。
int x, a;
x = (a = 3, 6*3); // a = 3, x = 18。
x = a = 3, 6*a; // a = 3, x = 3,逗點表達式的結果為18,但並未被使用。
int result = (x = a = 3, 6 * a); // result = 18。使用強制型態轉換時,則會抑制編譯器的報錯形為,故強制型態轉換一般不建議使用。
建議使用 C++ 風格。
相較於之前討論的『隱式型態轉換』,亦存在『顯式型態轉換』(又稱『強制型態轉換』)。如:
// int k = 5 % 3.2; // 錯誤: % 運算子兩側必須為整數型態。
int k = 5 % (int)3.2;
// 也可以寫成下列形式
int k2 = 5 % int(3.2);
()
:沒有形態的檢查,型態轉換的對錯由程式設計師負責。C++ 提供更安全的『強制型態轉換』:
static_cast
dynamic_cast
const_cast
reinterpret_cast
語法:強制型態轉換名稱<type>(expression);
static_cast:
『靜態轉換』,可視為一般轉換,與 C 語言的強制型態類似。
編譯期就會進行型態轉換的檢查。
double f = 100.123f;
int i = (int)f; // C語言風格。
int i2 = static_cast<int>(f); // 100, C++語言風格。子類別轉換為父類別時,也可以使用
static_cast。
class A // 父類別
{
};
class B:public A // 子類別
{
};
int main()
{
B b;
A a = static_cast<A>(b); // 允許將子類別轉換為父類別。反之,則錯誤。
return 0;
}void* 指標與其他型態指標之間的轉換:
void*
為『無型態指標』,也就是可以指向任何型態的指標。int i = 10;
int *p = &i;
void *q = static_cast<void*>(p);
int *db = static_cast<int*>(q);不可用於指標型態之間的轉換,如 int* 轉
double* 等。
double f = 100.0;
double *pf = &f;
// int *i = static_cast<int*>(pf); // 錯誤。
// float *fd = static_cast<float*>(pf); // 錯誤。dynamic_cast:
顧名思義,該轉換用於執行期間進行型態檢查。
主要用於父類別轉換為子類別。
成本較高,但可以確保型態轉換的安全性。
const_cast:
移除指標變數或參考的
const 的屬性。故功能較為單純。
編譯期進行此型態轉換檢查。
若原來變數為 const,當強制使用
const_cast 移除 const
屬性並修改其值,此為未定義行為,請避免之。
const int ai = 90;
//int ai2 = const_cast<int>(ai); // 錯誤:因ai不是指標,也不是參考,故無法轉換。
const int *pai = &ai;
int *pai2 = const_cast<int*>(pai); // 正確。
*pai2 = 120; // 建議不要這樣做!
std::cout << ai << std::endl;
std::cout << *pai << std::endl;reinterpret_cast:
可用來處理兩個型態無關的型態轉換。
編譯期進行此型態轉換檢查。
reinterpret_cast 與機器相依。
使用上,因編譯器不會提出任何警告與錯誤訊息,故具危險性。建議避免使用。
一般情況下,語句(statement)是按順序執行的。
C++ 提供一組控制流程語句來處理更複雜的程式執行邏輯。如條件語句、循環語句與中斷當下控制流程的跳轉語句等。
當表達式末尾加上分號(;)就成為語句。如:ival + 5;。
ival + 5; // 沒有實際作用的語句。
std::cout << ival; // 有意義的表達式語句。 空語句只有一個分號(;),為最簡單的語句。
; // 空語句。
用途:若程式在語法上需要一條語句,但邏輯實際上並不需要時,則可使用空語句。
// 持續讀入數據直至到達文件末尾或某次輸入的值等於sought
while(std::cin >> s && s != sought)
; // 空語句。注意分號的使用:不要漏寫,也不要多寫,故多餘的語句不代表無害。
ival = v1 + v2;; // 正確,第二個分號表達一條多餘的語句。
while(iter != svec.end()); // while循環體為『空語句』。
++iter; // 遞增運算子並不屬於while循環的一部分。複合語句(compound
statement)是指用『大括弧』括({})起來的(也可以為空)語句。又稱為『語句塊(block)』。
語句塊可視為一個『作用域(scope)』。
在語句塊內宣告的變數只能在該語句塊以及嵌套在該語句塊內被存取(可視 (visible))
若在程式某處,語法需要一條語句,但實際邏輯上需要多條語句,此時可使用『複合語句』。例如,while
語句或 for 語句:
while (val <= 10)
{
sum += val;
++val;
}可在 if、switch、 while 或
for
語句內的控制結構內定義變數。而在控制結構內定義的變數只能在對應的語句內可視。若要在外部程式需要使用控制變數,則變數必須定義在外部。
while (int i = get_num())
std::cout << i << std::endl;
i = 0; // 錯誤,在while外部無法存取i。
auto beg = v.begin(); // 變數定義在外部。
while (beg != v.end() && *beg >= 0)
++beg;
if (beg == v.end())
{
}if 語句根據條件來決定控制流程。
if 語句形式:
if (condition) // condition為真,則執行statement
statement
if (condition) // condition為真,則執行statement1,否則執行statement2。
statement1
else
statement2
condition 都必須被 () 包起來。
condition 可以是表達式,也可以初始化一個變數。無論是哪一種,都必須能轉換為『布林值』。
statement、statement1 與 statement2 都必須為語句塊。
當單一條語句無法滿足邏輯需求時,可加入大括弧行程語句快。
當 if 語句嵌套在另一個 if 語句時,請留意 else 與哪一個 if 匹配為一組:
// 請注意實際程式執行狀況與程式縮排格式的差別:
if (grade % 10 >= 3)
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
else
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
// 縮排格式與執行過程相符,但並非程式設計師意圖。
if (grade % 10 >= 3)
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
else
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
if (grade % 10 >= 3)
{
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
}
else // 大括弧強迫else與外層if匹配
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
switch 語句switch
後面的表達式可以是整數表達式,字元表達式,也可以是 enum
型態,或可轉換為整數型態,並根據此整數回傳值決定執行動作。
每個 case
後面的常數表達式彼此間必須不同,否則會出現編譯錯誤。
switch 語句形式:
switch (expr)
{
case 常數1:
語句1;
break;
case 常數2:
語句2;
break;
.....
case 常數n:
語句n;
break;
default:
default語句;
}switch 語句由以下4部分組成:
switch 表達式。
一組 case 條件。
與一個或一組 case
條件相關聯的語句或語句塊。
default 標籤。也可以不使用,但當所有 case
條件都不滿足時,則整個 switch 就不被執行。
若某個 case
條件匹配成功,將從該條件開始往後依序執行所有 case
分支。除非程式顯示中斷該過程,否則直到 switch
的結尾才會停止。
若要避免執行後續 case 分支,可在下一個
case 標籤之前加上 break 語句。
一般不要省略 case 分支最後的 break
語句。若不寫,請加上註釋說明之。
unsigned vowelCnt = 0;
switch (ch)
{
// 出現a、e、i、o、u中的任一個都會將vowelCnt的值加1。
case 'a'":
case 'e':
case 'i':
case 'o':
case 'u':
++vowelCnt;
break;
}goto 語句又稱『無條件轉向語句』。
也被視為『跳轉語句』。
用來跳轉到程式某個位置進行執行。
語法:
goto 語句標號;
_
三種字元組成,且第一個字元必須為字母或 _
,另外不可為系統保留字)。與 if 語句一起構成循環結構。
從循環體內部跳轉到循環體外部(但會破壞結構化程式設計原則,建議不使用)。
在大多數情況下,可以使用其他循環語句來代替 goto
語句。
goto 語句無法用來跨函數跳轉。
int i = 1, sum = 0;
loop: // 語句標號。
if (i <= 100)
{
sum = sum + i;
i++;
goto loop;
}
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);
loop: 為語句標號。後面接:。
當程式執行到 goto loop; 語句時可以直接跳轉至
loop 標號所在行並重新繼續往下執行,如此反覆。
void func1()
{
lbl1:
int k;
k = 1;
goto lbl1;
}
void func2()
{
lbl2:
int a;
a = 1;
}
func1 函數中的 goto lbl2; 修改為
goto lbl2;,這樣做會發生語法錯誤:因為 lbl2 位於 func2
函數內,goto 無法跨函數跳轉。while 語句語法:
while (表達式) 欲執行的語句;
先判斷表達式的值,若表達式值為真(非 0),則執行
欲執行的語句(循環體),然後再次循環回去,重新判斷表達式值,再決定是否再次欲執行的語句(循環體)部分,如此反覆。
若要執行多條語句,則使用 {} 括起來。
int i = 1, sum = 0;
while (i <= 100)
{
sum = sum + i;
i++;
}
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);do … while 語句語法:
do 要執行的語句 while (表達式);
要執行的語句,然後判斷表達式的值,若表達式之值為
true(非0)時,繼續執行循環體語句,然後繼續判斷表達式的值,如此反覆,直到表達式的值為
false(0),跳出整個do … while
語句,接著繼續往後面語句執行。至少會執行一次循環,然後才會進行判斷表達式是否為
true。
int i = 1, sum = 0;
do{
sum = sum + i;
++i;
} while (i <= 100);
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);do … while
語句使用情境較少,因為大多數情況下,do … while 語句可以被
while 語句來替代。
for 語句語法:for (表達式1; 表達式2; 表達式3) 內嵌語句;
for 語句執行步驟:
表達式1求值。
表達式2求值。
若表達式2之值為 true(非0),則執行 for
語句中的內嵌語句,接著執行表達式3,反覆循環步驟2,直到表達式2的值為
false(0),則循環結束,跳到整個 for
語句後面的語句來執行。
表達式1只會被執行一次。
循環執行幾次,表達式3就執行幾次。
int i, sum = 0;
for (i = 1; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。表達式1可以省略,但其後的分號(;)不能省略。
int i, sum = 0;
i = 1;
for ( ; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。表達式2可以省略,也就是不加入循環結束的條件,但分號(;)仍然不能省略。
int i, sum = 0;
for (i = 1; ; ++i)
{
sum = sum + i;
if (i >= 100)
break;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。表達式3可以省略,但必須保證循環能正常結束,否則循環會無止盡執行。
int i, sum;
for (sum = 0, i = 1; i <= 100; )
{
sum = sum + i;
++i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。三個表達式都省略:不設定初值,不判斷條件,循環變數不變動。如下所示:
while (1)
{
}
for(;;)
{
}
int i = 1, sum = 0;
for(;;)
{
sum = sum +i;
if (i >= 100)
break;
++i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
表達式1可以用來設定循環變數的初值,也可以設定與循環變數無關的其他表達式。
int i = 1, sum;
for (sum = 0; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。int k = 0;
for (size_t i = 2; i <= 9; i++)
{
for (size_t j = 1; j <= 9; j++)
{
k = i * j;
std::cout << i << " * " << j << " = " << k << std::endl;
}
std::cout << std::endl;
}
break 語句break 語句會中斷離它最近的
while、do .. while、for 或
switch
語句,並從這些語句後的第一條語句開始繼續執行。
break 語句只能出現在迭代語句或 switch
語句的內部。
break 語句通常與一個 if
語句配合使用。
for (size_t i = 0; i < 5; ++i)
{
if (i == 2)
break;
std::cout << i << std::endl;
}
int i, sum = 0;
for (i = 0; i < 100; i++)
{
sum = sum + i;
if (sum >= 4000)
break;
}
std::cout << "sum = " << sum << std::endl;
std::cout << "i = " << i << std::endl;
int i, j, k;
for (i = 1; i < 9; i++)
{
for (j = 1; j < i; j++)
{
k = i * j;
std::cout << i << " * " << j << " = " << k << std::endl;
break;html
}
printf("\n");
break;
}
std::cout << "流程結束!" << std::endl;continue 語句continue
語句終止最近循環中當下的迭代並立即開始下一次的迭代。
continue 語句只能用在 for、
while 與 do … while
循環的內部,或是嵌套在此類循環裡的語句或語句塊內部。
與 break 類似,當出現在嵌套循環中的
continue 語句僅作用在離它最近的循環。
與 break 不同,只有當 switch
嵌套在循環內,才能在 switch 內使用 continue
語句。
int i;
for (i = 1; i <= 100; ++i)
{
if (i % 3 == 0)
continue;
std::cout << i << std::endl;
}try語句與例外(異常)處理『例外(異常)』是指發生在程式執行時的異常行為,這些行為超出函數正常功能的範圍。
例如:失去資料庫的連結,或遇到意外的資料輸入等。
當程式某部分檢測到無法處理的問題時,就需要使用『例外處理』。例如:當失去資料庫連結時,會發出警告訊息。
例外處理:
throw 表達式:以 throw
表達式表示遇到無法處理的問題,則會稱之 『引發(raise )例外』。
try 語句塊:
語句以關鍵字 try 開始,並以一個或多個
catch 子句(catch clause)結束。
try 語句塊拋出的例外通常會被某個 catch
子句處理。
例外類別(exception class):用於 throw 表達式與相關
catch 子句間傳遞例外的具體資訊。
throw 表達式使用 throw 表達式來引發異常。
// #include <stdexcept>
int x = 100;
int y = 200;
std:: cout << (x > y) << std::endl;
if (x != y)
throw std::runtime_error(" x 必須與 y 相等。");
std::cout << 2 * x << std::endl;try 語句塊語法:
try {
program-statement
} catch (exception-declaration) {
handler-statement
} catch (exception-declaration) {
handler-statement
} // ....C++標準程式庫定義一組類別,用於報告標準程式庫函數遇到的問題。
分別定義在 4個標頭檔中:
exception 標頭檔
stdexcept 標頭檔
new 標頭檔
type_info 標頭檔