1. 程式人生 > >從頭讀《C++ Primer Plus》(4)

從頭讀《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中定義的值。