C++ primer 第四章習題

chapter4 表達式

練習

4.1.2節練習

練習4.1

  • 表達式 5 + 10 * 20 / 2 的求值結果是多少?

105

練習4.2

  • 根據4.12節中的表,在下述表達式的合理位置添加括號,使得添加括號後運算對象的組合順序與添加括號前一致。
(a) *vec.begin()            //*(vec.begin())
(b) *vec.begin() + 1        //(*(vec.begin())) + 1

4.1.3節練習

練習4.3

  • C++語言沒有明確規定大多數二元運算符的求值順序,給編譯器優化留下了餘地。這種策略實際上是在代碼生成效率和程序潛在缺陷之間進行了權衡,你認爲這可以接受嗎?請說出你的理由。

可以接受,爲編譯器和程序員留有了餘地。

4.2節練習

練習4.4

  • 在下面的表達式中添加括號,說明其求值的過程及最終結果。編寫程序編譯該(不加括號的)表達式並輸出結果驗證之前的推斷。
12 / 3 * 4 + 5 *15 + 24 % 4 / 2
((((12 / 3) * 4) + (5 * 15)) + ((24 % 4) / 2 )) = 91

練習4.5

  • 寫出下列表達式的求值結果。
(a) -30 * 3 + 21 / 5              (b) -30 + 3 * 21 / 5
(c) 30 / 3 * 21 % 5               (d) -30 / 3 * 21 % 4
//(a) : -86; (b) : -18; (c) : 0; (d) : -2;

練習4.6

  • 寫出一條表達式用於確定一個整數是奇數還是偶數。
i % 2 == 0;

練習4.7

  • 溢出是何含義?寫出三條將導致溢出的表達式。
//溢出是指計算的記過導致該數的值將超出數據類型所能表示的範圍。
short s = 65536;
short s = -59999;
char c = 12345;

4.3節練習

練習4.8

  • 說明在邏輯與、邏輯或及相等性運算符中運算對象求值的順序。

邏輯與運算,優先計算左側運算對象的值,僅當其爲真時,纔會計算右側運算對象的值。

邏輯或運算,優先計算左側運算對象的值,僅當其爲假時,纔會計算右側運算對象的值。

相等性運算無順序。

練習4.9

  • 解釋在下面的if語句中條件部分的判斷過程。
const cha *cp = "Hello World";
if (cp && *cp)
// 首先計算cp這個常量指針是否不爲空指針,若爲不爲空指針,則解引用cp,查看其是否爲空字符串。

練習4.10

  • 爲while 循環寫一個條件,使其從標準輸入中讀取整數,遇到 42 時停止。
int i;
while (cin >> i && i != 42)

練習4.11

  • 書寫一條表達式用於測試4個值a、b、c、d的關係,確保a大於b、b大於c、c大於d。
a >b && b > c && c > d;

練習4.12

  • 假設i、j 和k 是三個整數,說明表達式 i != j < k 的含義。

先判斷j是否小於k,返回其bool值後提升爲int整型1或0,再判斷i是否爲1或0。最後再返回一個bool值。

4.4節練習

練習4.13

  • 在下述語句中,當賦值完成後 i 和 d 的值分別是多少?
int i; double d;
(a)  d = i = 3.5;                 (b) i = d = 3.5;
//(a) d = 3.0; i = 3;     (b) i = 3 ; d = 3.5;
 

練習4.14

  • 執行下述 if 語句後將發生什麼情況?
if (42=i) //...      非法語句,編譯器報錯。賦值語句需左側運算對象可修改。而42爲字面值常量。
if (i=42) //...      若i非const變量,則恆爲真,執行if內部語句。
 

練習4.15

  • 下面的賦值是非法的,爲什麼?應該如何修改?
double dval; int ival; int *pi;
dval = ival = pi = 0;
賦值運算符依照右結合律。故pi賦值0,得到空指針,卻無法轉化爲int爲ival賦值,所以語句非法。
將pi作爲最左側的運算對象進行賦值即可。如pi = dval = ival = 0;
 

練習4.16

  • 儘管下面的語句合法,但它們實際執行的行爲可能和預期並不一樣,爲什麼?應該如何修改?
(a) if (p = getPtr() != 0)          (b) if (i = 1024)
(a) 由於賦值運算符的優先度較低,故getPtr()獲得的值將先與0進行比較,判斷是否爲空指針。再將結果的bool值賦值給p。應爲將p賦值的語句添加括號,提高優先級。改爲 if ((p = getPtr()) != 0)
(b) 若i不是const變量,則將i賦值爲1024後,i不爲0,則作爲if語句的判斷條件將始終爲真。若語句的本意是判斷i是否爲1024,則應修改爲 if (i == 1024)
 

4.5節練習

練習4.17

  • 說明前置遞增運算符和後置遞增運算符的區別。

前置遞增運算符將首先將運算對象自增,再返回自增後的值。可作爲左值。

後置遞增運算符將首先將運算對象當前值的副本返回,再將運算對象自增。僅可作爲右值,其返回對象僅是副本。

練習4.18

  • 如果第132頁那個輸出vector對象元素的while循環使用前置遞增運算符,將得到什麼結果?

將返回自增加1之後的地址再被解引用。從vector中的第二個對象開始輸出,也可能輸出負值。輸出最後一個元素時輸出的是vector的尾後迭代器,導致溢出。

練習4.19

  • 假設 ptr 的類型是指向 int 的指針、vec的類型是vector、ival的類型是int,說明下面的表達式是何含義?如果有表達式不正確,爲什麼?應該如何修改?
(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]
 
(a)首先判斷ptr是否爲空指針,若不爲空指針,判斷ptr指向的值是否爲0。並將ptr指向下一個地址。
(b)判斷ival和ival後的值是否不爲零。
(c)c++未規定<=運算符的計算順序,因此該表達式錯誤。應修改爲vec[ival] <= vec[ival + 1]。比較在vec中第ival個數的值是否小於等於第ival + 1個數的值。
 

4.6節練習

練習4.20

  • 假設iter的類型是vector::iterator, 說明下面的表達式是否合法。如果合法,表達式的含義是什麼?如果不合法,錯在何處?
(a) *iter++;                  //合法。返回iter指向的字符串,iter指向下一個字符串元素。
(b) (*iter)++;                //不合法,字符串沒有自增運算。
(c) *iter.empty()             //不合法。點運算符優先級更高,而迭代器對象沒有empty成員。
(d) iter->empty();            //合法。返回iter指向的字符串的empty成員。即判斷其是否爲空字符串。
(e) ++*iter;                  //不合法,字符串沒有自增運算。
(f) iter++->empty();          //合法。判斷iter指向的字符串是否爲空,並將iter指向下一個字符串元素。
 

4.7節練習

練習4.21

  • 編寫一段程序,使用條件運算符從 vector 中找到哪些元素的值是奇數,然後將這些奇數值翻倍。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> vec = { 1,2,3,4,5,6,7,8,9,10 };
    for (auto &i : vec){
        (i % 2 == 0) ? NULL : i *= 2 ;
    }
}
 

練習4.22

  • 本節的示例程序將成績劃分爲high pass、pass和fail三種,擴展該程序使其進一步將 60 分到 75 分之間的成績設定爲low pass。要求程序包含兩個版本:一個版本只使用條件運算符;另一個版本使用1個或多個if語句。哪個版本的程序更容易理解呢?爲什麼?
#include <string>
using namespace std;
int main()
{
    string finalgrade;
    int grade;
    finalgrade = (grade > 90) ? "high pass" : (grade > 75) ? "pass" : (grade < 60) ? "fail" : "low pass";
    if (grade > 90) finalgrade = "high pass";
    else if (grade > 75) finalgrade = "pass";
    else if (grade < 60) finalgrade = "fail";
    else finalgrade = "low pass";
}
 

if版本更易理解,其條件判斷清晰,有分行幫助斷句理解。條件運算符嵌套過後可讀性降低,難以理解。

練習4.23

  • 因爲運算符的優先級問題,下面這條表達式無法通過編譯。根據4.12節中的表(第147頁)指出它的問題在哪裏?應該如何修改?
string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s";
+運算符優先級比==運算符優先級高,則與字符比較的將是字符串pl,而兩者無法比較,故無法通過編譯。
string pl = s + (s[s.size() - 1] == 's' ? "" : "s");
 

練習4.24

  • 本節的示例程序將成績劃分爲 high pass、pass和fail三種,它的依據是條件運算符滿足右結合律。假如條件運算符滿足的是左結合律,求值的過程將是怎樣的?
    finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
 

優先判斷grade > 90,根據返回值來執行"high pass"grade < 60,並將其作爲最後一個條件運算符進行判斷。最終返回的結果將是“fail”"pass",不會是"high pass",因爲它被作爲條件用於判斷了。

4.8節練習

練習4.25

  • 如果一臺機器上int佔32位、char佔8位,用的是Latin-1字符集,其中字符’q’的二進制形式是01110001,那麼表達式’q’ << 6的值是什麼?

將先被提升爲32位int,再進行左移操作值爲00000000 00000000 00011100 01000000

練習4.26

  • 在本節關於測驗成績的例子中,如果使用unsigned int作爲quiz1的類型會發生什麼情況?

因爲unsigned int僅含16位,不足以以例子的方式存儲30位學生的信息,會發生溢出或信息丟失等。

練習4.27

  • 下列表達式的結果是什麼?
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2          //前29位均爲0,後3位爲011 即3。
(b) ul1 | ul2          //前29位均爲0,後3位爲111 即7。
(c) ul1 && ul2         //bool值 true
(d) ul1 || ul2         //bool值 true
 

4.9節練習

練習4.28

  • 編寫一段程序,輸出每一種內置類型所佔空間的大小。
#include <iostream>
using namespace std;
int main()
{
    cout << sizeof(bool) << '\n';
    cout << sizeof(char) << '\n';
    cout << sizeof(wchar_t) << '\n';
    cout << sizeof(char16_t) << '\n';
    cout << sizeof(char32_t) << '\n';
    cout << sizeof(short) << '\n';
    cout << sizeof(int) << '\n';
    cout << sizeof(long) << '\n';
    cout << sizeof(long long) << '\n';
    cout << sizeof(float) << '\n';
    cout << sizeof(double) << '\n';
    cout << sizeof(long double) << '\n';
}
 

練習4.29

  • 推斷下面代碼的輸出結果並說明理由。實際運行這段程序,結果和你想象的一樣嗎?如不一樣,爲什麼?
int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;    //輸出數組長度10。對數組運算將得到整個數組所佔空間大小。
cout << sizeof(p)/sizeof(*p) << endl;    //32位下爲1,64位下爲2。 因爲指針的大小在32位機上是4字節,在64位上是8字節。而int爲32位,4個字節。
 

練習4.30

  • 根據4.12節中的表(第147頁),在下述表達式的適當位置加上括號,使得加上括號之後表達式的含義與原來的含義相同。
(a) sizeof x + y        // (sizeof x) + y
(b) sizeof p->mem[i]    // sizeof((p->mem)[i])
(c) sizeof a < b        // (sizeof a) < b
(d) sizeof f()          // sizeof (f())
 

4.10節練習

練習4.31

  • 本節的程序使用了前置版本的遞增運算符和遞減運算符,解釋爲什麼要用前置版本而不用後置版本。要想使用後置版本的遞增遞減運算符需要做哪些改動?使用後置版本重寫本節的程序。

使用前置版本可以減少不必要的計算資源消耗。後置版本重寫直接將運算符改爲後置即可。

練習4.32

  • 解釋下面這個循環的含義。
constexpr int size = 5;
int ia[size] = {1, 2, 3, 4, 5};
for (int *ptr = ia, ix = 0; ix != size && ptr != ia + size; ++ix, ++ptr)
{/* ... */}
 

遍歷數組。

練習4.33

  • 根據4.12節中的表(第147頁)說明下面這條表達式的含義。
someValue ? ++x, ++y : --x, --y;
逗號運算符將分隔表達式,形成 (someValue ? ++x, ++y : --x), --y;
若someValue 爲真,自增x,捨棄x,自增y,捨棄y,自減y,返回y。
若someValue 爲假,自減x,捨棄x,自減y,返回y。
 

4.11.1節練習

練習4.34

  • 根據本節給出的變量定義,說明在下面的表達式中將發生什麼樣的類型轉換:
  • 需要注意每種運算符遵循的是左結合律還是右結合律。
(a) if (fval)             //fval轉化爲bool。若fval爲0,則轉化爲false,否則爲true。
(b) dval = fval + ival    //ival轉化爲float,計算後的值轉化爲doubal賦值給dval。
(c) dval + ival * cval    //cval轉化爲int,計算後的值轉爲爲double。
 

練習4.35

  • 假設有如下的定義,

    char cval;
    int ival;
    unsigned int ui;
    float fval;
    double dval;
    
    

    請回答在下面的表達式中發生了隱式類型轉換嗎?如果有,指出來。

(a) cval = 'a' + 3;               //發生了,'a'轉化爲int,計算後的值轉化爲char
(b) fval = ui - ival * 1.0;       //發生了,ival轉化爲float。若float佔的空間比unsigned int大,則ui轉化爲float。否則乘法計算後的值轉化爲unsigned int,最後轉化爲float。
(c) dval = ui * fval;             //發生了。若float佔的空間比unsigned int大,則ui轉化爲float。否則fval轉化爲unsigned int,最後轉化爲double。
(d) cval = ival + fval + dval;    //ival,fval轉化爲double,計算後的結果轉化爲char
 

4.11.3節練習

練習4.36

  • 假設i是int類型,d是double類型,書寫表達式 i=d 使其執行整數類型的乘法而非浮點類型的乘法。*
i *= static_cast<int>(d)
 

練習4.37

  • 用命名的強制類型轉換改寫下列舊式的轉換語句。
int i; double d; const string *ps; char *pc; void *pv;
(a) pv = (void*)ps;     改爲 pv = static_cast<void*>(const_cast<string*>(ps));
(b) i = int(*pc);       改爲 i = static_cast<int*>(pc);
(c) pv = &d;            改爲 pv = static_cast<void*>(&d);
(d) pc = (char*) pv;    改爲 pc = static_cast<char*>(pv);
 

練習4.38

  • 說明下面表達式的含義。
double slope = static_cast<double>(j / i);
 

將 j/i 的結果轉換爲 double,然後賦值給slope。

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