5.1 節練習
練習 5.1
空語句是最簡單的語句,空語句由一個單獨的分號構成。如果在程式的某個地方,語法上需要一條語句但是邏輯上不需要,此時應該使用空語句,空語句什麼也不做。
一種常見的情況是,當迴圈的全部工作在條件部分就可以完成時,我們通常會用到空語句。使用空語句時最好加上註釋,從而令程式碼的閱讀者知道這條語句是有意省略內容的。
練習 5.2
塊是指用花括號括起來的語句和宣告的序列,也稱為複合語句。一個塊就是一個作用域,在塊中引入的名字只能在塊內部以及巢狀在塊中的子塊裡訪問。如果在程式的某個地方,語法上需要一條語句,但是邏輯上需要多條語句,此時應該使用塊。塊不需要以分號結束。
例如,迴圈體必須是一條語句,但是我們通常需要在迴圈體內做很多事情,此時就應該把多條語句用花括號括起來,從而把語句序列轉變成塊。
練習 5.3
原文的 while 迴圈使用了塊,其形式是:
while ( val <= 10 )
{
sum += val;
++val;
}
利用逗號運算子改寫之後的形式如下所示:
while ( val <= 10 )
sum += val, ++val;
很明顯,改寫之後的程式碼不夠清晰,可讀性降低了。
5.2 節練習
練習 5.4
(a)是非法的,它的原意是希望在 while 語句的控制結構當中定義一個string::iterator 型別的變數 iter,然後判斷 iter 是否到達了 s 的末尾,只要還沒有到達末尾就執行迴圈體的內容。但是該式把變數的定義和關係判斷混合在了一起,如果要使用 iter 與其他值比較,必須首先為 iter 賦初值。修改後的程式應該是:
string::iterator iter=s.begin();
while (iter != s.end())
{
++iter;
/*...*/
}
(b)是非法的,變數 status 定義在 while 迴圈控制結構的內部,其作用域僅限於 while 迴圈。if 語句已經位於 while 迴圈的作用域之外,status 在 if 語句內是一個未命名的無效變數。要想在 if 語句中繼續使用 status,需要把它定義在while 迴圈之前。修改後的程式應該是:
bool status;
while (status = find(word)) { /* ... */ }
if (!status) { /* ... */ }
附上書上原話:
5.3 節練習
練習 5.5
#include <iostream>
using namespace std;
int main()
{
int grade;
cout << "請輸入您的成績:";
cin >> grade;
if (grade < 0 || grade > 100)
{
cout << "該成績不合法" << endl;
return -1;
}
if (grade == 100) // 處理滿分的情況
{
cout << "等級成績是:" << "A++" << endl;
return -1;
}
if (grade < 60) // 處理不及格的情況
{
cout << "等級成績是:" << "F" << endl;
return -1;
}
int iU = grade / 10; // 成績的十位數
int iT = grade % 10; // 成績的個位數
string score, level, lettergrade;
// 根據成績的十位數字確定 score
if (iU == 9)
score = "A";
else if (iU == 8)
score = "B";
else if (iU == 7)
score = "C";
else
score = "D";
// 根據成績的個位數字確定 level
if (iT < 3)
level = "-";
else if (iT > 7)
level = "+";
else
level = "";
// 累加求得等級成績
lettergrade = score + level;
cout << "等級成績是:" << lettergrade << endl;
return 0;
}
輸出如下:
練習 5.6
#include <iostream>
using namespace std;
int main()
{
int grade;
cout << "請輸入您的成績:";
cin >> grade;
if (grade < 0 || grade > 100)
{
cout << "該成績不合法" << endl;
return -1;
}
if (grade == 100) // 處理滿分的情況
{
cout << "等級成績是:" << "A++" << endl;
return -1;
}
if (grade < 60) // 處理不及格的情況
{
cout << "等級成績是:" << "F" << endl;
return -1;
}
int iU = grade / 10; // 成績的十位數
int iT = grade % 10; // 成績的個位數
string score, level, lettergrade;
// 根據成績的十位數字確定 score
score = (iU == 9) ? "A"
: (iU == 8) ? "B"
: (iU == 7) ? "C" : "D";
// 根據成績的個位數字確定 level
level = (iT < 3) ? "-"
: (iT > 7) ? "+" : "";
// 累加求得等級成績
lettergrade = score + level;
cout << "等級成績是:" << lettergrade << endl;
return 0;
}
輸出如下:
練習 5.7
(a) if 語句的迴圈體應該是一條語句,需要以分號結束,
程式修改為:
if (ival1 != ival2)
ival1 = ival2;
else ival1 = ival2 = 0;
(b) if 語句的迴圈體只能是一條語句,本題從邏輯上來說需要做兩件事,一是修改 minval 的值,二是重置 occurs 的值,所以必須把這兩條語句放在一個塊裡。
程式修改為:
if (ival < minval)
{
minval = ival;
occurs = 1;
}
(c) ival 是定義在 if 語句中的變數,其作用域僅限於第一個 if 語句,要想在第二個 if 語句中也使用它,就必須把它定義在兩個 if 語句的外部。
程式修改為:
int ival;
if (ival = get_value())
cout << "ival = " << ival << endl;
if (!ival)
cout << "ival = 0\n";
(d) 程式的原意是判斷 ival 的值是否是 0,原題使用賦值運算子的結果是把 0賦給了 ival,然後檢驗 ival 的值,這樣使得條件永遠不會滿足。
程式修改為:
if (ival == 0)
ival = get_value();
練習 5.8
懸垂 else 是指當程式中的 if 分支多於 else 分支時,如何為 else 尋找與之匹配的 if 分支的問題。
C++規定,else 與離它最近的尚未匹配的 if 匹配,從而消除了二義性。
練習 5.9
#include <iostream>
using namespace std;
int main()
{
unsigned int vowelCnt = 0;
char ch;
cout << "請輸入一段文字:" << endl;
while (cin >> ch)
{
if (ch == 'a')
++vowelCnt;
if (ch == 'e')
++vowelCnt;
if (ch == 'i')
++vowelCnt;
if (ch == 'o')
++vowelCnt;
if (ch == 'u')
++vowelCnt;
}
cout << "您輸入的文字中共有 " << vowelCnt << "個母音字母" << endl;
return 0;
}
輸出如下:
練習 5.10
#include <iostream>
using namespace std;
int main()
{
unsigned int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
cout << "請輸入一段文字:" << endl;
while (cin >> ch)
{
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
}
}
cout << "母音字母 a 的數量是:" << aCnt << endl;
cout << "母音字母 e 的數量是:" << eCnt << endl;
cout << "母音字母 i 的數量是:" << iCnt << endl;
cout << "母音字母 o 的數量是:" << oCnt << endl;
cout << "母音字母 u 的數量是:" << uCnt << endl;
return 0;
}
輸出如下:
練習 5.11
#include <iostream>
using namespace std;
int main()
{
unsigned int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
unsigned int spaceCnt = 0, tabCnt = 0, newlineCnt = 0;
char ch;
cout << "請輸入一段文字:" << endl;
while (cin.get(ch))
{
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case ' ':
++spaceCnt;
break;
case '\t':
++tabCnt;
break;
case '\n':
++newlineCnt;
break;
}
}
cout << "母音字母 a 的數量是:" << aCnt << endl;
cout << "母音字母 e 的數量是:" << eCnt << endl;
cout << "母音字母 i 的數量是:" << iCnt << endl;
cout << "母音字母 o 的數量是:" << oCnt << endl;
cout << "母音字母 u 的數量是:" << uCnt << endl;
cout << "空格的數量是:" << spaceCnt << endl;
cout << "製表符的數量是:" << tabCnt << endl;
cout << "換行符的數量是:" << newlineCnt << endl;
return 0;
}
輸出如下:
注意:
讀入字元的語句應該使用 cin.get(ch)
,而不能使用 >>
,因為後者會忽略本題所要統計的特殊符號。
如果寫成 while (cin >> ch)
,則輸出結果是這樣:
練習 5.12
我們的設定是一個字元只會被統計一次。如果使用者輸入的序列是 xxxxxfflxxx,則統計結果是 ff:1 次、fl:0 次、fi:0 次。如果使用者輸入的序列是xxxxxfiffffflxxx,則統計結果是 ff:2 次、fl:1 次、fi:1 次。
滿足題意的程式如下所示:
#include <iostream>
using namespace std;
int main()
{
unsigned int ffCnt = 0, flCnt = 0, fiCnt = 0;
char ch, prech = '\0';
cout << "請輸入一段文字:" << endl;
while (cin >> ch)
{
bool bl = true;
if (prech == 'f')
{
switch (ch)
{
case 'f':
++ffCnt;
bl = false;
break;
case 'l':
++flCnt;
break;
case 'i':
++fiCnt;
break;
}
}
if (!bl)
prech = '\0';
else
prech = ch;
}
cout << "ff 的數量是:" << ffCnt << endl;
cout << "fl 的數量是:" << flCnt << endl;
cout << "fi 的數量是:" << fiCnt << endl;
return 0;
}
輸出如下:
練習 5.13
switch 語句有幾個語法要點:必須在必要的地方使用 break;語句,應該把變數定義在塊作用域內,case 標籤只能有一個值且不能是變數。
(a)的錯誤是在每個 case 分支中都缺少了 break;語句,造成的後果是一旦執行了前面的 case 分支,必定還會繼續執行接下來的其他 case 分支。舉例說明,如果ch 的內容是字元'a',則 aCnt、eCnt 和 iouCnt 的值都會增加;如果 ch 的內容是字元'e',則 eCnt 和 iouCnt 的值都會增加,這顯然與程式的預期是不相符的。
修改後的程式如下所示:
unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a':
aCnt++;
break;
case 'e':
eCnt++;
break;
default:
iouCnt++;
break;
}
(b)的錯誤是在 case 分支中定義並初始化了變數 ix,同時在 default 分支中使用了該變數,此時如果控制流跳過 case 分支而直接到達 default 分支,則會試圖使用未經初始化的變數,因而該程式無法透過編譯。解決辦法是,把 ix 的定義放置在 switch 語句之前。
修改後的程式如下所示:
unsigned index = some_value();
int ix;
switch (index) {
case 1:
ix = get_value();
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;
ivec[ix] = index;
}
附上書上原話:
插句題外話
在Visual Studio 2019 中:
如果寫成這樣,則編譯會報錯
#include <iostream>
#include <vector>
using namespace std;
int main()
{
unsigned index = 2;
vector<int> ivec(5);
int ix;
switch (index) {
case 1:
//int ix = 10;
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;
ivec[ix] = index;
}
for (auto i : ivec)
cout << i << " ";
cout << endl;
return 0;
}
但是寫成這樣(即在定義ix的同時對其初始化)就可以編譯透過了:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
unsigned index = 2;
vector<int> ivec(5);
int ix = 0;
switch (index) {
case 1:
//int ix = 10;
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;
ivec[ix] = index;
}
for (auto i : ivec)
cout << i << " ";
cout << endl;
return 0;
}
輸出如下:
在Dev-C++ 5.11中:
寫成這樣(即在定義ix的時候不對其進行初始化),是不報錯的:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
unsigned index = 2;
vector<int> ivec(5);
int ix;
switch (index) {
case 1:
//int ix = 10;
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;
ivec[ix] = index;
}
for (auto i : ivec)
cout << i << " ";
cout << endl;
return 0;
}
輸出如下:
在Visual Studio 2019中,寫成這樣,會報如下錯誤:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
unsigned index = 2;
vector<int> ivec(5);
switch (index) {
case 1:
int ix = 10;
ivec[ix] = index;
break;
default:
ix = ivec.size() - 1;
ivec[ix] = index;
}
for (auto i : ivec)
cout << i << " ";
cout << endl;
return 0;
}
迴歸正題
(c)的錯誤是在同一個 case 標籤中放置了多個值,而 C++規定一個 case 標籤只能對應一個值。
修改後的程式如下所示:
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1:
case 3:
case 5:
case 7:
case 9:
oddcnt++;
break;
case 2:
case 4:
case 6:
case 8:
case 10:
evencnt++;
break;
}
(d)的錯誤是使用變數作為 case 標籤的內容,C++規定,case 標籤的內容只能是整型常量表示式。
修改後的程式如下所示:
const unsigned ival = 512, jval = 1024, kval = 4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch (swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
5.4 節練習
練習 5.14
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 定義 3 個字串變數,分別表示:
// 當前操作的字串、前一個字串、當前出現次數最多的字串
string currString, preString = "", maxString;
// 定義 2 個整型變數,分別表示:
// 當前連續出現的字串數量、當前出現次數最多的字串數量
int currCnt = 1, maxCnt = 0;
while (cin >> currString) // 檢查每個字串
{
// 如果當前字串與前一個字串一致,更新狀態
if (currString == preString)
{
++currCnt;
if (currCnt > maxCnt)
{
maxCnt = currCnt;
maxString = currString;
}
}
// 如果當前字串與前一個字串不一致,重置 currCnt
else
{
currCnt = 1;
}
// 更新 preString 以便於下一次迴圈使用
preString = currString;
}
if (maxCnt > 1)
cout << "出現最多的字串是:" << maxString
<< ",次數是:" << maxCnt << endl;
else
cout << "每個字串都只出現了一次" << endl;
return 0;
}
輸出如下:
插句題外話
自己寫的
#include <iostream>
using namespace std;
int main()
{
int num = 1, res = 1;
string word, preword, maxword;
while (cin >> word)
{
if (preword == word)
{
num++;
}
else
{
if (num > res)
{
res = num;
maxword = preword;
}
preword = word;
num = 1;
}
}
res == 1 ? cout << "no answer" << endl : cout << maxword << " " << res << endl;
return 0;
}
輸出如下:
練習 5.15
(a)的錯誤是在 for 語句中定義了變數 ix,然後試圖在 for 語句之外繼續使用ix。因為 ix 定義在 for 語句的內部,所以其作用域僅限於 for 語句。在 if 語句中 ix 已經失效,因此程式無法編譯透過。
修改後的程式如下:
int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// ...
(b)的錯誤有兩個,一是變數 ix 未經初始化就直接使用,二是 for 語句的控制結構缺少一句話,在語法上是錯誤的。
修改後的程式如下:
int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */ }
(c)的錯誤是一旦進入迴圈,程式就會無休止地執行下去。也就是說,當初始情況下 ix != sz 時,由題意可知 ix 和 sz 一直同步增長,迴圈的終止條件永遠不會滿足,所以該迴圈是一個死迴圈。
修改後的程式如下:
for (int ix = 0; ix != sz; ++ix) { /* ... */ }
練習 5.16
不贅敘了
練習 5.17
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1 = { 0, 1, 1, 2 };
vector<int> v2 = { 0, 1, 1, 2, 3, 5, 8 };
vector<int> v3 = { 3, 5, 8 };
vector<int> v4 = { 3, 5, 0, 9, 2, 7 };
auto it1 = v1.cbegin(); // 定義 v1 的迭代器
auto it2 = v2.cbegin(); // 定義 v2 的迭代器
auto it3 = v3.cbegin(); // 定義 v3 的迭代器
auto it4 = v4.cbegin(); // 定義 v4 的迭代器
// 設定迴圈條件:v1 和 v2 都尚未檢查完
while (it1 != v1.cend() && it2 != v2.cend())
{
// 如果當前位置的兩個元素不相等,則肯定沒有字首關係,退出迴圈
if (*it1 != *it2)
{
cout << "v1 和 v2 之間不存在字首關係" << endl;
break;
}
++it1; // 迭代器移動到下一個元素
++it2; // 迭代器移動到下一個元素
}
if (it1 == v1.cend()) // 如果 v1 較短
{
cout << "v1 是 v2 的字首" << endl;
}
if (it2 == v2.cend()) // 如果 v2 較短
{
cout << "v2 是 v1 的字首" << endl;
}
return 0;
}
輸出如下:
程式的輸出結果是:v1 是 v2 的字首。如果更新程式使其處理 v3 和 v4,則程式將顯示:v3 和 v4 之間不存在字首關係。
插句題外話
自己寫的
#include <iostream>
#include <vector>
using namespace std;
bool isequal(vector<int> vshort, vector<int> vlong)
{
for (int index = 0; index != vshort.size(); index++)
if (vshort[index] != vlong[index])
return false;
return true;
}
int main()
{
vector<int> v1;
vector<int> v2;
int n, size1, size2;
cout << "輸入第一組資料的長度:";
cin >> size1;
cout << "輸入第一組資料:";
for (int i = 0; i < size1; ++i)
{
cin >> n;
v1.push_back(n);
}
cout << "輸入第二組資料的長度:";
cin >> size2;
cout << "輸入第二組資料:";
for (int i = 0; i < size2; ++i)
{
cin >> n;
v2.push_back(n);
}
if (size1 <= size2)
isequal(v1, v2) ? cout << "v1是v2的字首" << endl
: cout << "v1 和 v2 之間不存在字首關係" << endl;
else
isequal(v1, v2) ? cout << "v2是v1的字首" << endl
: cout << "v1 和 v2 之間不存在字首關係" << endl;
return 0;
}
輸出如下:
練習 5.18
(a)的含義是每次迴圈讀入兩個整數並輸出它們的和。因為 do-while 語句的迴圈體必須是一條語句或者一個語句塊,所以在本題中應該把迴圈體的內容用花括號括起來。
修改後的程式是:
do {
int v1, v2;
cout << "Please enter two numbers to sum:";
if (cin >> v1 >> v2)
cout << "Sum is: " << v1 + v2 << endl;
} while (cin);
(b)的含義是當 get_response 的返回值不為 0 時執行迴圈體。因為 do-while語句不允許在迴圈條件內定義變數,所以該程式是錯誤的。
修改後的程式是:
int ival;
do {
ival = get_response();
} while (ival);
(c) 的含義是當 get_response 的返回值不為 0 時執行迴圈體。因為出現在do-while 語句條件部分的變數必須定義在迴圈體之外,所以該程式是錯誤的。
修改後的程式是:
int ival;
do {
ival = get_response();
} while (ival);
練習 5.19
#include <iostream>
#include <string>
using namespace std;
int main()
{
do {
cout << "請輸入兩個字串" << endl;
string str1, str2;
// 檢查輸入是否有效
if (!(cin >> str1 >> str2)) {
// 輸入失敗時退出迴圈
cout << "輸入結束或發生錯誤。" << endl;
break;
}
// 比較字串長度並輸出結果
if (str1.size() < str2.size())
cout << "長度較小的字串是:" << str1 << endl;
else if (str1.size() > str2.size())
cout << "長度較小的字串是:" << str2 << endl;
else
cout << "兩個字串等長" << endl;
} while (true); // 迴圈控制交由輸入校驗處理
return 0;
}
輸出如下:
在自己寫這題的時候,思考了一個問題,可以看看這篇部落格 使用while迴圈分別對兩個vector進行賦值,該怎麼做
5.5 節練習
練習 5.20
#include <iostream>
#include <string>
using namespace std;
int main()
{
string currString, preString;
bool bl = true;
cout << "請輸入一組字串:" << endl;
while (cin >> currString)
{
if (currString == preString)
{
bl = false;
cout << "連續出現的字串是:" << currString << endl;
break;
}
preString = currString;
}
if (bl)
cout << "沒有連續出現的字串" << endl;
return 0;
}
輸出如下:
練習 5.21
#include <iostream>
#include <string>
using namespace std;
int main()
{
string currString, preString;
bool bl = true;
cout << "請輸入一組字串:" << endl;
while (cin >> currString)
{
if (!isupper(currString[0]))
{
preString = "";
continue;
}
if (currString == preString)
{
bl = false;
cout << "連續出現的以大寫字母開頭的字串是:" << currString << endl;
break;
}
preString = currString;
}
if (bl)
cout << "沒有連續出現的以大寫字母開頭的字串" << endl;
return 0;
}
練習 5.22
int sz;
do {
sz = get_size();
} while (sz <= 0);
5.6 節練習
練習 5.23
#include <iostream>
using namespace std;
int main()
{
cout << "請依次輸入被除數和除數:" << endl;
int ival1, ival2;
cin >> ival1 >> ival2;
if (ival2 == 0)
{
cout << "除數不能為 0" << endl;
return -1;
}
cout << "兩數相除的結果是:" << ival1 / ival2 << endl;
return 0;
}
輸出如下:
練習 5.24
#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
cout << "請依次輸入被除數和除數:" << endl;
int ival1, ival2;
cin >> ival1 >> ival2;
if (ival2 == 0)
{
throw runtime_error("除數不能為 0");
}
cout << "兩數相除的結果是:" << ival1 / ival2 << endl;
return 0;
}
在Visual Studio 2019中執行,彈出了這個:
在Dev C++中執行,輸出如下:
練習 5.25
#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
cout << "請依次輸入被除數和除數:" << endl;
int ival1, ival2;
while (cin >> ival1 >> ival2)
{
try
{
if (ival2 == 0)
{
throw runtime_error("除數不能為 0");
}
cout << "兩數相除的結果是:" << ival1 / ival2 << endl;
}
catch (runtime_error err)
{
cout << err.what() << endl;
cout << "需要繼續嗎(y or n)?";
char ch;
cin >> ch;
if (ch != 'y' && ch != 'Y')
break;
}
}
return 0;
}
輸出如下: