4.1 節練習
練習 4.1
在算術運算子中,乘法和除法的優先順序相同,且均高於加減法的優先順序。因此上式的計算結果應該是 105,在程式設計環境中很容易驗證這一點。
練習 4.2
在本題涉及的運算子中,優先順序最高的是成員選擇運算子和函式呼叫運算子,其次是解引用運算子,最後是加法運算子。因此新增括號後的等價的式子是:
(a) *(vec.begin())
(b) (*(vec.begin())) + 1
下述程式可以用來驗證我們的推斷:
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
vector<int> vec;
srand((unsigned)time(NULL));
cout << "系統自動為向量生成一組元素......" << endl;
for (int i = 0; i != 10; i++)
vec.push_back(rand() % 100);
cout << "生成的向量資料是:" << endl;
for (auto c : vec)
cout << c << " ";
cout << endl;
cout << "驗證新增的括號是否正確:" << endl;
cout << "*vec.begin()的值是:" << *vec.begin() << endl;
cout << "*(vec.begin())的值是:" << *(vec.begin()) << endl;
cout << "*vec.begin()+1 的值是:" << *vec.begin() + 1 << endl;
cout << "(*(vec.begin()))+1 的值是:" << (*(vec.begin())) + 1 << endl;
return 0;
}
輸出如下:
練習 4.3
正如題目所說,C++只規定了非常少的二元運算子(邏輯與運算子、邏輯或運算子、條件(?:)運算子、逗號運算子)的求值順序,其他絕大多數二元運算子的求值順序並沒有明確規定。這樣做提高了程式碼生成的效率,但是可能引發潛在的缺陷。
關鍵是缺陷的風險有多大?我們知道,對於沒有指定執行順序的運算子來說,
如果表示式指向並修改了同一個物件,將會引發錯誤併產生未定義的行為;而如果運算物件彼此無關,它們既不會改變同一物件的狀態也不執行IO任務,則函式的呼叫順序不受限制。
就作者的觀點而言,這樣的做法在一定程度上是可以接受的,前提是在編寫程
序時注意以下兩點:一是拿不準的時候最好用括號來強制讓表示式的組合關係符合程式邏輯的要求;二是一旦改變了某個運算物件的值,在表示式的其地方就不要再使用這個運算物件了。
附上書上原話:
4.2 節練習
練習 4.4
不贅敘了
練習 4.5
本題用到兩條典型的算術運算規則。
一是除法:整數相除結果還是整數,如果商含有小數部分,直接棄除。尤其當除法的兩個運算物件的符號不同時,商為負,C++11 新標準規定商一律向 0 取整。
二是取餘:如果取餘的兩個運算物件的符號不同,則負號所在的位置不同運算結果也不相同,m%(-n)等於 m%n,(-m)%n 等於-(m%n)。
因此,本題的求值結果是:
(a) -30 * 3 + 21 / 5 = -86
(b) -30 + 3 * 21 / 5 = -18
(c) 30 / 3 * 21 % 5 = 0
(d) -30 / 3 * 21 % 4 = -2
附上書上原話:
練習 4.6
下面的表示式可以用於確定一個整數是奇數還是偶數,假設該整數名為 num,則表示式 num % 2 == 0
為真時 num 是偶數,該表示式為假時 num 是奇數。
練習 4.7
溢位是一種常見的算術運算錯誤。因為在計算機中儲存某種型別的記憶體空間有限,所以該型別的表示能力(範圍)也是有限的,當計算的結果值超出這個範圍時,就會產生未定義的數值,這種錯誤稱為溢位。
假定編譯器規定 int 佔 32 位,則下面的 3 條表示式都將產生溢位錯誤:
int i = 2147483647 + 1;
int j = -100000 * 300000;
int k = 2024 * 2024 * 2024 * 2024;
顯然與我們的預期不相符,是溢位之後產生的錯誤結果。
4.3 節練習
練習 4.8
對於邏輯與運算子來說,當且僅當兩個運算物件都為真時結果為真;對於邏輯或運算子來說,只要兩個運算物件中的一個為真結果就為真。
邏輯與運算子和邏輯或運算子都是先求左側運算物件的值再求右側運算物件的值,當且僅當左側運算物件無法確定表示式的結果時才會計算右側運算物件的值。
這種策略就是短路求值。其策略是:對於邏輯與運算子來說,當且僅當左側運算物件為真時才計算右側運算物件;對於邏輯或運算子來說,當且僅當左側運算物件為假時才計算右側運算物件。
值得注意的是,邏輯與運算子和邏輯或運算子是 C++中僅有的幾個規定了求值順序的運算子。相等性運算子的兩個運算物件都需要求值,C++沒有規定其求值順序。
練習 4.9
cp 是指向字串的指標,因此上式的條件部分含義是首先檢查指標 cp 是否有效。如果 cp 為空指標或無效指標,則條件不滿足。如果 cp 有效,即 cp 指向了記憶體中的某個有效地址,繼續解引用指標 cp 並檢查 cp 所指的物件是否為空字元'\0',如果 cp 所指的物件不是空字元則條件滿足;否則不滿足。
在本例中,顯然初始狀態下 cp 指向了字串的首字元,是有效的;同時當前cp 所指的物件是字元'H',不是空字元,所以 if 的條件部分為真。
練習 4.10
最簡潔的形式是:
while( cin >> num && num != 42 )
該語句首先檢查從輸入流讀取資料是否正常,然後判斷當前讀入的數字是否是42,遇到 42 則條件不滿足,退出迴圈。
還有一種形式也可以實現同樣的目的:
int num;
while( cin >> num )
{
if(num == 42)
break;
// 其他操作
}
練習 4.11
要想用一條表示式測試 a、b、c、d 的關係,並確保 a 大於 b、b 大於 c、c 大於 d,應該寫成:
a > b && b > c && c > d
切勿寫成:
a > b > c > d
因為關係運算子滿足左結合律且運算的結果是布林值,所以把幾個關係運算子連寫在一起必然會產生意想不到的結果。
a > b > c > d
的實際求值過程是先判斷 a > b 是否成立,成立則為1,不成立則為 0;接著用這個布林值(1 或 0)與 c比較,所得的結果仍然是一個布林值;最後再用剛剛得到的布林值與 d 進行比較。
顯然這一過程與使用者的書寫原意背道而馳。
練習 4.12
C++規定<、<=、>、>=的優先順序高於==和!=,因此上式的求值過程等同於i!=(j<k)
,意即先比較 j 和 k 的大小,得到的結果是一個布林值(1 或 0);然後判斷 i 的值與之是否相等。
附上書上原話:
4.4 節練習
練習 4.13
由題意可知,(a)式的含義是先把 3.5 賦值給整數 i,此時發生了自動型別轉換,小數部分被捨棄,i 的值為 3;接著 i 的值再賦給雙精度浮點數 d,所以 d 的值也是3。
(b)式的含義是先把 3.5 賦值給雙精度浮點數 d,因此 d 的值是 3.5;接著 d 的值再賦給整數 i,此時發生了自動型別轉換,小數部分被捨棄,i 的值為 3。
本題涉及的知識點有兩個:
第一,如果賦值運算子左右兩個運算物件的型別不同,則右側運算物件轉換成左側運算物件的型別;
第二,賦值運算子滿足右結合律。
附上書上原話:
練習 4.14
第一條語句發生編譯錯誤,因為賦值運算子的左側運算物件必須是左值,字面值常量 42 顯然不是左值,不能作為左側運算物件。
第二條語句從語法上來說是正確的,但是與程式的原意不符。程式的原意是判斷 i 的值是否是 42,應該寫成 i==42;而 i=42 的意思是把 42 賦值給 i,然後判斷i 的值是否為真。因為所有非 0 整數轉換成布林值時都對應 true,所以該條件是恆為真的。
本題涉及的知識點有兩個:
第一,賦值運算子的左側運算物件必須是左值,右側運算物件可以是左值,也可以是右值;
第二,賦值運算子與相等性運算子在作為 if 語句的條件時含義不同。
練習 4.15
該賦值語句是非法的,雖然連續賦值的形式本身並沒有錯,但是參與賦值的幾個變數型別不同。其中,dval 是雙精度浮點數,ival 是整數,pi 是整型指標。
自右向左分析賦值操作的含義,pi=0 表示 pi 是一個空指標,接下來ival=pi
試圖把整型指標的值賦給整數,這是不符合語法規範的操作,無法編譯透過。稍作調整,就可以把上述程式改為合法。
double dval; int ival; int *pi;
dval = ival = 0;
pi = 0;
練習 4.16
(a)的原意是把 getPtr()得到的指標賦值給 p,然後判斷 p 是否是一個空指標,但上述表示式的實際執行結果與之相距甚遠。因為賦值運算子的優先順序低於不相等運算子,所以真正的表示式求值過程是先判斷 getPtr()的返回值是否為空指標,如果是則 p=0,否則 p=1,最後以 p 的值作為 if 語句的條件。
要想符合原意,應該修改為:
if ( (p = getPtr()) != 0)
(b)的原意是判斷 i 的值是否是 1024,但上述表示式實際上是把 1024 賦值給 i,然後以 i 作為 if 語句的條件。因為所有非 0 整數轉換成布林值時都對應 true,所以該條件是恆為真的。
附上書上原話:
4.5 節練習
練習 4.17
遞增和遞減運算子有兩種形式:前置版本和後置版本。
前置版本首先將運算物件加 1(或減 1),然後把改變後的物件作為求值結果。後置版本也將運算物件加 1(或減 1),但是求值結果是運算物件改變之前那個值的副本。
這兩種運算子必須作用於左值運算物件。前置版本將物件本身作為左值返回,後置版本則將物件原始值的副本作為右值返回。
我們的建議是,除非必須,否則不用遞增(遞減)運算子的後置版本。前置版本的遞增運算子避免了不必要的工作,它把值加 1 後直接返回改變了的運算物件。與之相比,後置版本需要將原始值儲存下來以便於返回這個未修改的內容。如果我們不需要修改之前的值,那麼後置版本的操作就是一種浪費。
對於整數和指標型別來說,編譯器可能對這種額外的工作進行了一定的最佳化;但是對於相對複雜的迭代器型別來說,這種額外的工作就消耗巨大了。建議養成使用前置版本的習慣,這樣不僅不需要擔心效能問題,而且更重要的是寫出的程式碼會更符合程式設計人員的初衷。
練習 4.18
程式碼如下:
前置遞增運算子先將運算物件加 1,然後把改變後的物件作為求值結果;後置遞增運算子也將運算物件加 1,但是求值結果是運算物件改變之前那個值的副本。
簡言之,如果在一條表示式中出現了遞增運算子,則其計算規律是:++在前,先加 1,後參與運算;++在後,先參與運算,後加 1。
基於上述分析,本題不應該把 while 迴圈的後置遞增運算子改為前置遞增運算子。如果這樣做了,會產生兩個錯誤結果:一是無法輸出 vector 物件的第一個元素;二是當所有元素都不為負時,移動到最後一個元素的地方,程式試圖繼續向前移動迭代器並解引用一個根本不存在的元素。
練習 4.19
(a)的含義是先判定指標 ptr 是否為空,如果不為空,繼續判斷指標 ptr 所指的整數是否為非 0 數。如果非 0,則該表示式的最終求值結果為真;否則為假。最後把指標 ptr 向後移動一位。該表示式從語法上分析是合法的,但是最後的指標移位操作不一定有意義。如果 ptr 所指的是整型陣列中的某個元素,則 ptr 可以按照預期移動到下一個元素。如果 ptr 所指的只是一個獨立的整數變數,則移動指標操作將產生未定義的結果。
(b)的含義是先檢查 ival 的值是否非 0,如果非 0 繼續檢查(ival+1)的值是否非 0。只有當兩個值都是非 0 值時,表示式的求值結果為真;否則為假。在 4.1.3 節中我們學習到,如果二元運算子的兩個運算物件涉及同一個物件並改變物件的值,則這是一種不好的程式寫法,應該改寫。所以按照程式的原意,本式應該改寫成 ival && (ival + 1)
。
(c)的含義是比較 vec[ival]
和 vec[ival + 1]
的大小,如果前者較小則求值結果為真,否則為假。與(b)式一樣,本式也出現了二元運算子的兩個運算物件涉及同一個物件並改變物件值的情況,應該改寫為 vec[ival] <= vec[ival + 1]
。
4.6 節練習
練習 4.20
(a)是合法的,後置遞增運算子的優先順序高於解引用運算子,其含義是解引用當前迭代器所處位置的物件內容,然後把迭代器的位置向後移動一位。
(b)是非法的,解引用iter得到vector物件當前的元素,結果是一個string,顯然 string 沒有後置遞增操作。
(c)是非法的,解引用運算子的優先順序低於點運算子,所以該式先計算 iter.empty()
,而迭代器並沒有定義 empty 函式,所以無法透過編譯。
(d)是合法的,iter->empty();
等價於 (*iter).empty();
。解引用迭代器得到迭代器當前所指的元素,結果是一個 string,顯然字串可以判斷是否為空,empty 函式在此處有效。
(e)是非法的,該式先解引用 iter,得到迭代器當前所指的元素,結果是一個string,顯然 string 沒有後置遞增操作。
(f)是合法的,iter++->empty();等價於(*iter++).empty();。含義是解引用迭代器當前位置的物件內容,得到一個字串,判斷該字串是否為空,然後把迭代器向後移動一位。
對於(e),附上書上原話:
4.7 節練習
練習 4.21
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
vector<int> vInt;
const int sz = 10; // 使用 sz 作為陣列的維度
srand((unsigned)time(NULL)); // 生成隨機數種子
// 使用普通 for 迴圈為陣列賦初值
cout << "陣列的初始值是:" << endl;
for (int i = 0; i != sz; ++i)
{
vInt.push_back(rand() % 100); // 生成 100 以內的隨機數
cout << vInt[i] << " "; // 使用下標運算子輸出陣列內容
}
cout << endl;
// 使用範圍 for 迴圈把陣列中的奇數翻倍
for (auto& val : vInt)
val = (val % 2 != 0) ? val * 2 : val; // 條件表示式
// 使用普通 for 迴圈和迭代器輸出陣列的當前值
cout << "調整後的陣列值是:" << endl;
for (auto it = vInt.cbegin(); it != vInt.cend(); ++it)
cout << *it << " ";
cout << endl;
return 0;
}
輸出如下:
練習 4.22
使用條件運算子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string finalgrade;
int grade;
cout << "請輸入您要檢查的成績:" << endl;
// 確保輸入的成績合法
while (cin >> grade && grade >= 0 && grade <= 100)
{
// 使用三層巢狀的條件表示式
finalgrade = (grade > 90) ? "high pass"
: (grade > 75) ? "pass"
: (grade > 60) ? "low pass" : "fail";
cout << "該成績所處的檔次是:" << finalgrade << endl;
cout << "請輸入您要檢查的成績:" << endl;
}
return 0;
}
輸出如下:
使用 if 語句:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string finalgrade;
int grade;
cout << "請輸入您要檢查的成績:" << endl;
// 確保輸入的成績合法
while (cin >> grade && grade >= 0 && grade <= 100)
{
// 使用 if 語句實現
if (grade > 90)
finalgrade = "high pass";
else if (grade > 75)
finalgrade = "pass";
else if (grade > 60)
finalgrade = "low pass";
else
finalgrade = "fail";
cout << "該成績所處的檔次是:" << finalgrade << endl;
cout << "請輸入您要檢查的成績:" << endl;
}
return 0;
}
輸出如下:
練習 4.23
題目中的幾個運算子的優先順序次序從高到低是加法運算子、相等運算子、條件運算子和賦值運算子,因此式子的求值過程是先把 s 和 s[s.size() - 1]相加得到一個新字串,然後該字串與字元's'比較是否相等,這是一個非法操作,並且與程式的原意不符。
要想實現程式的原意,即先判斷字串 s 的最後一個字元是否是's',如果是,什麼也不做;如果不是,在 s 的末尾新增一個字元's',我們應該新增括號強制限定運算子的執行順序。
string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;
練習 4.24
原文的程式是:
finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
根據左結合律的含義,該式等價於:
finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";
先考查 grade > 90 是否成立,如果成立,第一個條件表示式的值為"high pass";如果不成立,第一個條件表示式的值為 grade < 60。這條語句是無法編譯透過的,因為條件運算子要求兩個結果表示式的型別相同或者可以互相轉化。即使假設語法上透過,也就是說,第一個條件表示式的求值結果分為 3 種,分別是"high pass"、1 和 0。接下來根據第一個條件表示式的值求解第二個條件表示式,求值結果是"fail"或"pass"。上述求值過程顯然與我們的期望是不符的。
4.8 節練習
練習 4.25
在位運算子中,運算子~的優先順序高於<<,因此先對 q 按位求反,因為位運算子的運算物件應該是整數型別,所以字元'q'首先轉換為整數型別。如題所示,char 佔 8 位而 int 佔 32 位,所以字元'q'轉換後得到 00000000 0000000 00000000 01110001,按位求反得到 11111111 11111111 11111111 10001110;接著執行移位操作,得到11111111 11111111 11100011 10000000。
C++規定整數按照其補碼形式儲存,對上式求補,得到 10000000 0000000 00011100 10000000,即最終結果的二進位制形式,轉換成十進位制形式是-7296。
練習 4.26
根據題目的要求,班級中有 30 個學生,我們用一個二進位制位代表某個學生在一次測驗中是否透過,則原書中使用 unsigned long 作為 quiz1 的資料型別是恰當的,因為 C++規定 unsigned long 在記憶體中至少佔 32 位,這樣就足夠存放 30 個學生的資訊。
如果使用 unsigned int 作為 quiz1 的型別,則由於 C++規定unsigned int所佔空間的最小值是 16,所以在很多機器環境中,該資料型別不足以存放全部學生的資訊,從而造成了資訊丟失,無法完成題目要求的任務。
練習 4.27
ul1 轉換為二進位制形式是:00000000 00000000 00000000 00000011,ul2 轉換為二進位制形式是:00000000 00000000 00000000 00000111。各個式子的求值結果分別是:
(a)按位與,結果是:00000000 00000000 00000000 00000011,即 3。
(b)按位或,結果是:00000000 00000000 00000000 00000111,即 7。
(c)邏輯與,所有非 0 整數對應的布林值都是 true,所以該式等價於 true && true,結果為 true。
(d)邏輯或,所有非 0 整數對應的布林值都是 true,所以該式等價於 true || true,結果為 true。
4.9 節練習
練習 4.28
#include <iostream>
using namespace std;
int main()
{
cout << "型別名稱\t" << "所佔空間" << endl;
cout << "bool\t\t" << sizeof(bool) << endl;
cout << "char\t\t" << sizeof(char) << endl;
cout << "wchar_t\t\t" << sizeof(wchar_t) << endl;
cout << "char16_t\t" << sizeof(char16_t) << endl;
cout << "char32_t\t" << sizeof(char32_t) << endl;
cout << "short\t\t" << sizeof(short) << endl;
cout << "int\t\t" << sizeof(int) << endl;
cout << "long\t\t" << sizeof(long) << endl;
cout << "long long\t" << sizeof(long long) << endl;
cout << "float\t\t" << sizeof(float) << endl;
cout << "double\t\t" << sizeof(double) << endl;
cout << "long double\t" << sizeof(long double) << endl;
return 0;
}
輸出如下:
練習 4.29
sizeof(x)的運算物件 x 是陣列的名字,求值結果是整個陣列所佔空間的大小,等價於對陣列中所有的元素各執行一次 sizeof 運算並對所得結果求和。尤其需要注意,sizeof 運算子不會把陣列轉換成指標來處理。在本例中,x 是一個 int陣列且包含 10 個元素,所以 sizeof(x)的求值結果是 10 個 int 值所佔的記憶體空間總和。
sizeof(*x)
的運算物件 *x
是一條解引用表示式,此處的 x 既是陣列的名稱,也表示指向陣列首元素的指標,解引用該指標得到指標所指的內容,在本例中是一個 int。所以 sizeof(*x)在這裡等價於 sizeof(int),即 int 所佔的記憶體空間。
sizeof(x)/sizeof(*x)
可以理解為陣列 x 所佔的全部空間除以其中一個元素所佔的空間,得到的結果應該是陣列 x 的元素總數。實際上,因為 C++的內建陣列並沒有定義成員函式 size(),所以通常無法直接得到陣列的容量。本題所示的方法是計算得到陣列容量的一種常規方法。
sizeof(p)的運算物件 p 是一個指標,求值結果是指標所佔的空間大小。
sizeof(*p)
的運算物件 *p
是指標 p 所指的物件,即 int 變數 x,所以求值結果是 int 值所佔的空間大小。
在Visual Studio 2019 中執行結果如下:
在該編譯環境中int 佔 4 位元組,指標也佔 4 位元組
在 Dev-C++ 中執行結果如下:
在該編譯環境中 int 佔 4 位元組,指標佔 8 位元組。
練習 4.30
(a)的含義是先求變數 x 所佔空間的大小,然後與變數 y 的值相加;因為 sizeof 運算子的優先順序高於加法運算子的優先順序,所以如果想求表示式 x+y 所佔的記憶體空間,應該改為 sizeof(x + y)。
(b)的含義是先定位到指標 p 所指的物件,然後求該物件中名為 mem 的陣列成員第 i 個元素的尺寸。因為成員選擇運算子的優先順序高於 sizeof 的優先順序,所以本例無須新增括號。
(c)的含義是先求變數 a 在記憶體中所佔空間的大小,再把求得的值與變數 b 的值比較。因為 sizeof 運算子的優先順序高於關係運算子的優先順序,所以如果想求表示式 a<b
所佔的記憶體空間,應該改為 sizeof(a < b)。
(d)的含義是求函式 f()返回值所佔記憶體空間的大小,因為函式呼叫運算子的優先順序高於 sizeof 的優先順序,所以本例無須新增括號。
4.10 節練習
練習 4.31
本題從程式執行結果來說,使用前置版本或後置版本是一樣的,這是因為遞增遞減運算子與真正使用這兩個變數的語句位於不同的表示式中,所以不會有什麼影響。
使用後置版本重寫的程式是:
vector<int>::size_type cnt = ivec.size();
// 將從 size 到 1 的值賦給 ivec 的元素。
for(vector<int>::size_type ix = 0;ix != ivec.size(); ix++, cnt--)
ivec[ix] = cnt;
根據 4.5 節的介紹我們知道,除非必須,否則不用遞增(遞減)運算子的後置版本。前置版本的遞增運算子避免了不必要的工作,它把值加 1 後直接返回改變了的運算物件。與之相比,後置版本需要將原始值儲存下來以便於返回這個未修改的內容。如果我們不需要修改之前的值,那麼後置版本的操作就是一種浪費。
就本題而言,使用前置版本是更好的選擇。
練習 4.32
首先定義一個常量表示式 size,它的值是 5;接著以 size 作為維度建立一個整型陣列 ia,5 個元素分別是 1~5。
for 語句頭包括三部分:第一部分定義整型指標指向陣列 ia 的首元素,並且定義了一個整數 ix,賦給它初值 0;第二部分判斷迴圈終止的條件,當 ix 沒有達到size 同時指標 ptr 沒有指向陣列最後一個元素的下一位置時,執行迴圈體;第三部分令變數 ix 和指標 ptr 分別執行遞增操作。
練習 4.33
C++規定條件運算子的優先順序高於逗號運算子,所以 someValue ? ++x, ++y : --x, --y
實際上等價於 (someValue ? ++x, ++y : --x), --y
。它的求值過程是,首先判斷 someValue 是否為真,如果為真,依次執行++x 和 ++y,最後執行--y;如果為假,執行--x 和--y。
檢驗一下:
#include <iostream>
using namespace std;
int main()
{
int x = 10, y = 20;
// 檢驗條件為真的情況
bool someValue = true;
someValue ? ++x, ++y : --x, --y;
cout << x << endl;
cout << y << endl;
cout << someValue << endl;
x = 10, y = 20;
// 檢驗條件為假的情況
someValue = false;
someValue ? ++x, ++y : --x, --y;
cout << x << endl;
cout << y << endl;
cout << someValue << endl;
return 0;
}
輸出如下:
當 someValue 取值為 true 時,依次執行++x、++y、--y,也就是說,x 的值加 1 變為 11,y 的值先加 1 後減 1 保持不變,還是 20。
當 someValue 取值為 false 時,依次執行--x、--y,x 和 y 的值各減少 1 變為 9 和 19。
4.11 節練習
練習 4.34
(a)if 語句的條件應該是布林值,因此 float 型變數 fval 自動轉換成布林值,轉換規則是所有非 0 值轉換為 true,0 轉換為 false。
(b)ival轉換成float,與fval求和後所得的結果進一步轉換為double型別。
(c)cval 執行整型提升轉換為 int,與 ival 相乘後所得的結果轉換為 double型別,最後再與 dval 相加。
練習 4.35
(a)字元'a'提升為 int,與 3 相加所得的結果再轉換為 char 並賦給cval。
(b)ival 轉換為 double,與 1.0 相乘的結果也是 double 型別,ui 轉換為double 型別後與乘法得到的結果相減,最終的結果轉換為 float 並賦給 fval。
(c)ui 轉換為 float,與 fval 相乘的結果轉換為 double 型別並賦給 dval。
(d)ival 轉換為 float,與 fval 相加所得的結果轉換為 double 型別,再與dval 相加後結果轉換為 char 型別。
練習 4.36
使用 static_cast 把 double 型別的變數 d 強制轉換成 int 型別,就可以令表示式 i*=d 執行整數型別的乘法。語句的形式應該是:
i *= static_cast<int>(d);
驗證一下:
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
int i = 2;
double d = 3.5;
cout << (i *= static_cast<int>(d)) << endl;
cout << typeid(i *= static_cast<int>(d)).name() << endl;
return 0;
}
輸出如下:
練習 4.37
(a) pv = static_cast<void*> (const_cast<string*> (ps));
(b) i = static_cast<int>(*pc);
(c) pv = static_cast<void*>(&d);
(d) pc = static_cast<char*>(pv);
練習 4.38
把 j/i 的值強制型別轉換成 double,然後賦值給 slope。請注意,如果 i 和 j 的型別都是 int,則 j/i 的求值結果仍然是 int,即使除不盡也只保留商的整數部分,最後再轉換成 double 型別。