第五章

hisun9發表於2024-11-17

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) { /* ... */ }

附上書上原話:

img

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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

注意

讀入字元的語句應該使用 cin.get(ch),而不能使用 >>,因為後者會忽略本題所要統計的特殊符號。

如果寫成 while (cin >> ch),則輸出結果是這樣:

img

練習 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;
}

輸出如下:

img

練習 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;
}

附上書上原話:

img

插句題外話

在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;
}

img

但是寫成這樣(即在定義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;
}

輸出如下:

img

在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;
}

輸出如下:

img

在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;
}

img

迴歸正題

(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;
}

輸出如下:

img

插句題外話

自己寫的

#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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

程式的輸出結果是: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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

在自己寫這題的時候,思考了一個問題,可以看看這篇部落格 使用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;
}

輸出如下:

img

練習 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;
}

輸出如下:

img

練習 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中執行,彈出了這個:

img

在Dev C++中執行,輸出如下:

img

練習 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;
}

輸出如下:

img

相關文章