從頭讀《C++ Primer Plus》(4)

第五章 循環與關係表達式

小目錄

  • for循環
  • 表達式和語句
  • 增減操作符:++和--
  • 賦值操作符組合
  • 複合語句(區塊)
  • 逗號運算符
  • 關係運算符:>,>=,==,<=,<和!=
  • while循環
  • typedef功能
  • do while循環
  • get()字符輸入方法
  • end-of-file(文件尾)狀況
  • 嵌套循環和二維數組

for循環介紹

示例代碼:

#include <iostream>
int main()
{
    using namespace std;
    int i;
    for(i = 0;i < 5;i++)
        cout << "loop" << i << endl;
    cout << "loop end." << endl;
}

輸出:

loop0
loop1
loop2
loop3
loop4
loop end.

for循環從i=0開始,這是循環初始化(loop initialization)部分。然後是循環測試(loop test),在這部分程序會測試i<5是否爲真。如果爲真,則執行循環內的代碼,也即是執行循環體(loop body)。再之後執行循環更新(loop update)部分,將i的值加一。

for循環的每個部分

for循環通常包含以下部分:

  1. 值初始化
  2. 進行測試以決定是否繼續循環
  3. 執行循環操作
  4. 更新測試用到的值

其格式爲:

for(initialization;test-expression;update-expression)

    body

表達式和語句

C++中任意值或任意有效的值和操作符的組合都可以組成爲表達式。而每個表達式都含有一個值。一般來說這個值會比較明顯。比如22+27這個表達式的值就是49.但有一些的值會不那麼明顯,比如x=10.C++將一個賦值表達式的值定義爲其左邊的成員,所以x=10的值爲10.這就使得連續賦值成爲可能。

x = y = 10;  //y=10的值爲10,左式相當於y=10,x=y

而最後關於關係表達式比如x>y,它們的值則是布爾類型值true或false。

非表達式和語句

雖說給表達式加上分號就可以得到一條語句。但把一條語句去掉分號並不總能得到一個表達式。比如int a;是一條語句,去掉分號得到的int a卻不是一個表達式。故而它也沒有值。你不能採用下列的寫法:

b = int a;

cout << int a;

同樣的,for循環也不是一個表達式。

摺疊規則

其實for循環裏的測試用變量不一定要在循環初始化前聲明,而可以直接在循環初始化部分聲明,如下:

for(int i = 0;i < 5;i++)

改變步幅

至今寫的for循環的循環更新部分都是將測試變量加(減)1,但實際上我們是可以任意對測試變量進行更新的,簡單的比如加2:

for(int i = 0;i < 5;i = i + 2)

增減運算符

在上面寫的for循環有用到了增減運算符++和--。其中++實際上就是C++這個名字的來源。

這兩個操作符有兩種用法,那就是放在值的前面或後面。其不同在於對值的操作是在值被應用前還是應用後。看如下示例代碼:

#include <iostream>
int main()
{
	using namespace std; 
    int a = 1, b = 1;
    int c = a++, d = ++b;
    cout << "a:" << a << " b:" << b 
	<< " c:" << c << " d:" << d << endl;
}

輸出:

a:2 b:2 c:1 d:2

可以看出,表達式a++的值是a增值後的值2。而表達式++b的值則是b增值前的值1.

邊效應(side effect)和序列點(sequence point)

首先,邊效應是在表達式進行變更,比如對變量,的時候起作用。而序列點則是邊效應的作用結束點。在C++裏,語句末的分號就起到序列點的作用。也即是所有的包括賦值操作,增減操作都必須在該句的分號之前完成。同樣,一個完整表達式的末尾也是一個序列點。

完整表達式是指一個不作爲另一個表達式的子表達式的表達式。完整表達式包括表達式語句的表達式部分還有循環測試部分的表達式。

關於前後綴

當你不使用表達式a++的值的時候,a++和++a似乎沒有任何區別,但實際上,這二者在效率上有着輕微的不同。對於C++自有類型自然就無關緊要,但C++同樣允許你將這個運算符定義給類。而這時就要注意前置和後置的區別。前置運算符的步驟是先把操作對象增值,然後將其返回。而後置則是將對象複製,然後將其增值,在將複製的對象返回。也即是後置運算符會多出一步複製。在對複雜對象操作時就會產生效率差距。不過對於自有類型就沒有太大關係了。

增減運算符與指針

對指針同樣可以使用++與--。其效果將是使指針指向的地址增(減)一個它指向的類型的長度。

而當你同時對指針使用++和*時,就涉及到優先級問題。你究竟是讓地址增值還是讓地址內的變量增值?粗略來說就是這兩個運算符有着相同優先級,它們遵循從右至左原則。

具體來講就是:

++*a = ++(*a)

*++a = *(++a)

*a++ = *(a++)

但要注意的是,後置運算符雖然先運算,但a++這個表達式的值是a增值前的值。也即是*a++的取到的值與*a相同,只是*a++結束後a的值加一。

賦值操作符組合

a += b;   //a = a + b

a -= b;   //a = a - b

a *= b;   //a = a * b

a /= b;   //a = a / b

a %= b;   //a = a % b

複合語句(區塊)

for循環的循環體只能寫一句似乎並不能滿足需要。所以C++允許使用大括號來構建一個複合語句或者說區塊。區塊由一對大括號和其中的多句代碼組成。一整個區塊可以算作一條語句。所以最開始的for循環示例可以改成:

#include <iostream>
int main()
{
    using namespace std;
    int i;
    for(i = 0;i < 5;i++)
    {
        cout << "loop" ;
        cout << i << endl;
    }
    cout << "loop end." << endl;
}

逗號操作符

C++允許你通過區塊在只允許寫一條語句的地方塞進去多條語句。而對於表達式,C++同樣提供了逗號操作符在達成相同效果,在只允許寫一個表達式的地方寫多個表達式。比如for循環頭部的三個組成部分,每個部分都只允許寫一個表達式。而逗號操作符就可以在當中添加多個表達式:

for(int i = 0;i < 5;j++, i++)

但逗號並不總是一個逗號操作符,有時它僅僅是用於分隔變量列表裏的名字:

int i,j;  //僅用於分隔

關係表達式

關係表達式用於進行值的比較,C++提供了六種關係操作符用於比較數字。而字符由於char類型裏存的實際是ASCII碼,所以也同樣可以用關係操作符。關係操作符不能用於C式字符串,但可以用於string類。每個關係表達式會返回一個布爾值,表達式爲真時返回true,否則返回false。

關係操作符
操作符 作用
<

小於

<= 小於等於
== 等於
> 大於
>= 大於等於
!= 不等於

C式字符串比較

比如說有個C式字符串word,其值爲“mate”,那麼或許你會覺得以下表達式爲真:

word == “mate”

但實際上要記住數組名指代的是數組地址。同樣的,雙引號括起來的字符串常量也指代其地址。以上語句中兩個字符串地址自然是不同的,所以表達式也就不會爲真。

如果你需要比較兩個C式字符串,你需要使用C式字符串庫裏的strcmp方法。這個方法接收兩個字符串作爲參數。其參數可以是指針,字符串常量,字符數組名。如果兩個字符串相同,那麼方法返回0.如果第一個字符串從字母表來說先於第二個字符串,那麼方法返回一個負數,反之返回正數。其實更準確來說應該是按系統字符表碼來排。比如兩個字符串的ASCII碼大小。因爲在ASCII中大寫字母的碼是小於小寫字母的,也即是首先大小寫是不同的,其次大寫字母是先於小寫字母的。所以“Zoo”是先於“abcd”的。

在某些語言比如BASIC裏,存儲於不同長度字符數組裏的相同字符串是不相等的。但C++裏的C式字符串比較是以字符串的空字符爲結尾的,所以以下兩個字符串對於strcmp是相等的:

char long[80] = "abc";

char short[4] = "abc";

還有,雖然不能對C式字符串使用關係運算符,但你可以對字符使用。

string類對象比較

string類對象可以用關係操作符比較是因爲類允許通過“重載”(overload)或者說重定義來定義操作符。十二章將會講到如何在類設計時引入這個特性。


while循環

while循環是for循環去掉了初始化和測試參數更新部分。格式:

while(test-condition)
    body

do while循環

這個循環和前兩個不同在於,它是在退出時進行測試(exit-condition)。也即是這個循環會先把循環體執行一遍,然後再進行測試。這種循環總是會執行至少一次。格式:

do
    body
while(test-expression)

面向範圍的for循環(C++11)

C++11新添了面向範圍的for循環(range-based for loop)。它主要是針對常見的需求——對數組內每個元素進行操作——提供了簡化的for循環。例子:

double d[5] = {1.0, 1.1, 1.2, 1.3, 1.4};
for (double dou : d)
    cout << dou << endl;

如果要對數組元素進行操作,則需要改寫爲:

for(double &dou : d)
    dou = dou + 2;

&符號將dou標識爲一個引用變量。這部分會在第八章講到。

這種for循環還能用於初始化列表:

for(int i : {1, 2, 3, 4})
    cout << i;

二維數組

C++並沒有提供特別的二維數組類型。而是通過定義一個數組的數組來創建一個二維數組。即數組內每個元素是一個數組。

聲明格式:

int two_dimen [4] [5] = 
{
    {1, 2, 3, 4, 5},
    {1, 2, 3, 4, 5},
    {1, 2, 3, 4, 5},
    {1, 2, 3, 4, 5}
};

總結

C++提供了三種循環:for循環、while循環和do while循環。只要循環測試的值爲true或非零,循環就會反覆執行同樣的一個代碼塊,而測試值爲false或0循環就會終止。for循環和while循環是進入測試的循環,也即是在每次執行前進行測試。而do while循環式退出測試的循環,也即是在每次執行後進行測試。

每個循環的循環體由一條語句組成。但這條語句可以是使用大括號括起的複合語句(或稱爲代碼塊)。

循環測試通常是一條關係表達式,這條語句會比對兩個值。關係表達式使用六種關係運算符:<,<=,==,>,>=,!=。關係表達式的值爲true或false。

許多程序從鍵盤輸入或文件逐個字符地讀入。可以使用cin >> ch的方式進行逐個字符讀入,但這種方式會跳過空格、換行符和縮進。如果需要嚴格讀入每個字符則需要用到cin.get(ch)。而沒有參數的get方法則會返回其獲取的字符,所以其用法應爲ch = cin.get()。

cin.get(char)方法在會在遇到eof(end-of-file)時返回一個false,而cin.get()則返回值EOF,一個在iostream中定義的值。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章