C ++ Primer(第五版)第四章练习答案

4.1.2 节练习

练习 4.1

表达式 5 + 10 * 20 / 2 的求值结果是多少?

105

练习 4.2

根据 4.12 节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。

*vec.begin()
*vec.begin() + 1

填括号

*(vec.begin())
(*(vec.begin())) + 1

4.1.3 节练习

练习 4.3

C++ 语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。

可以接受。提高 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

写出一条表达式用于确定一个整数是奇数还是偶数。

a % 2 == 0;

练习 4.7

溢出是何含义?写出三条将导致溢出的表达式。

溢出是指计算的结果超出该类型所能表示的范围时产生的情况。

short s = 44444;
short s = -44444;
int a = 12345678900;

4.3 节练习

练习 4.8

说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。

逻辑与运算:
优先计算左侧运算对象的值,仅当其为真时,才会计算右侧运算对象的值;
逻辑或运算:
优先计算左侧运算对象的值,仅当其为假时,才会计算右侧运算对象的值;
相等性运算无顺序。

练习 4.9

解释在下面的 if 语句中条件部分的判断过程。

const cha *cp = "Hello World";
if (cp && *cp)

先判断 cp 是否为空指针,如果不为空指针,再解引用,判断是否为空字符串。

练习 4.10

为 while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。

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,将结果提升为 int 的 0 或 1,再判断 i 是否等于 0 或 1,结果是 bool 值的 0 或 1。

4.4 节练习

练习 4.13

在下述语句中,当赋值完成后 i 和 d 的值分别是多少?

int i; double d;
(a)  d = i = 3.5;                 (b) i = d = 3.5;

(a) d = 3.0
(b) i = 3

练习 4.14

执行下述 if 语句后将发生什么情况?

if (42=i) //...      
if (i=42) //...      

非法;
若 i 为非常量,则恒为真。

练习 4.15

下面的赋值是非法的,为什么?应该如何修改?

double dval; int ival; int *pi;
dval = ival = pi = 0;

不能将指针的值赋值给 int;将 pi 改为 *pi。

练习 4.16

尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?

(a) if (p = getPtr() != 0)          (b) if (i = 1024)

(a)由于赋值运算的优先度低于 != ,会先将 getPtr() 得到的值与 0 对比,将结果的 bool 值赋值给 p;改为:

if ((p = getPtr()) != 0)

(b)若 i 不为非常量,判断始终为真;改为:

if (i == 1024)

4.5 节练习

练习 4.17

说明前置递增运算符和后置递增运算符的区别。

前置递增运算符将首先将运算对象自增,再返回自增后的值。可作为左值。
后置递增运算符将首先将运算对象当前值的副本返回,再将运算对象自增。仅可作为右值,其返回对象仅是副本。

练习 4.18

如果第 132 页那个输出 vector 对象元素的 while 循环使用前置递增运算符,将得到什么结果?

将返回自增加1之后的地址再被解引用。从 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 的值是否为 0 并加 1,若为 0 ,再判断加 1 之后的 ival 是否为 0;
( c ) 未规定<=运算符的计算顺序,因此该表达式错误。改成 vec[ival+1] <= vec[ival]。

4.6 节练习

练习 4.20

假设 iter 的类型是 vector< string >::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?

(a) *iter++;                  
(b) (*iter)++;                
(c) *iter.empty()             
(d) iter->empty();            
(e) ++*iter;                  
(f) iter++->empty();          

( a ) 合法;解引用 iter,得到指向的字符串,并指向下一个元素;
( b ) 不合法;解引用 iter,得到指向的字符串,并将字符串自增,字符串没有自增运算;
( c ) 不合法;点运算优先级高于解引用,iter 没有 empty 成员函数;
( d ) 合法;iter 所指向的值是否为空;
( e ) 不合法;字符串不能自增;
( f ) 合法;判断 iter 指向的字符串是否为空,并指向下一个元素。

4.7 节练习

练习 4.21

编写一段程序,使用条件运算符从 vector< int > 中找到哪些元素的值是奇数,然后将这些奇数值翻倍。

#include<iostream>
#include<vector>

using std::cout;
using std::vector;

int main()
{
    vector<int> vec = {0, 1, 2, 3, 4, 5, 6};
    for (auto &i : vec)
    {
        i = (i % 2) ? i * 2 : i;
    }
    for(auto i:vec) 
		cout << i << " ";

    return 0;
}

练习 4.22

本节的示例程序将成绩划分为 high pass、pass 和 fail 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用 1 个或多个 if 语句。哪个版本的程序更容易理解呢?为什么?

#include<iostream>
#include<vector>
#include<string>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    int grade;
    cin >> grade;
    string finalgrade = (grade > 90) ? "high pass" : (grade > 75) ? "pass" : (grade > 60) ? "low pass" : "fail";
    cout << finalgrade << endl;

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

if 版本更易理解,其条件判断清晰,有分行帮助断句理解。条件运算符嵌套过后可读性降低,难以理解。

练习 4.23

因为运算符的优先级问题,下面这条表达式无法通过编译。根据 4.12 节中的表(第 147 页)指出它的问题在哪里?应该如何修改?

string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s";

+ 的优先度高于 == 和 ?,故先执行 s + s[s.size() - 1],再执行比较,但字符串和字符不能比较。
改为:

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,再左移 6 位,结果为:

00000000 00000000 00011100 01000000

练习 4.26

在本节关于测验成绩的例子中,如果使用 unsigned int 作为quiz1 的类型会发生什么情况?

unsigned int 只有 16 位,不能记录 30 个学生的成绩。

练习 4.27

下列表达式的结果是什么?

unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2          
(b) ul1 | ul2          
(c) ul1 && ul2         
(d) ul1 || ul2         

前28位全为0,0011,3
前28位全为0,0111,7
true
true

4.9 节练习

练习 4.28

编写一段程序,输出每一种内置类型所占空间的大小。

#include<iostream>

using std::cout;
using std::endl;

int main()
{
    cout << sizeof(int) << endl;
    cout << sizeof(char) << endl;
    cout << sizeof(short) << endl;
    cout << sizeof(float) << endl;
    cout << sizeof(double) << endl;
    cout << sizeof(long) << endl;
    cout << sizeof(long long) << endl;

    return 0;
}

练习 4.29

推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?

int x[10];   int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(p)/sizeof(*p) << endl;

第一行是整个数组所占的空间大小(40)/一个元素所占的空间大小(int:4)=数组的个数(10);
第二行是指针类型的大小(32 位是 4 字节,64 位是 8 字节)/指针指向的对象类型(int:4)=(32 位下 1、64 位下 2)。

练习 4.30

根据 4.12 节中的表(第 147 页),在下述表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同。

(a) sizeof x + y      
(b) sizeof p->mem[i]  
(c) sizeof a < b     
(d) sizeof f()  

加括号

(a) (sizeof x) + y      
(b) sizeof (p->mem[i])  
(c) (sizeof a) < b     
(d) 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) { /* ... */ }

遍历数组,ptr 是指针方式遍历,ix 是下标方式遍历。

练习 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)
(b) dval = fval + ival;
(c) dval + ival * cval;

fval 转换成 bool;
ival 转换成 float,计算后的值转化为 doubal 赋值给 dval;
cval 转换成 int,计算的值转换成 double。

练习 4.35

假设有如下的定义:

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

请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。

(a) cval = 'a' + 3;
(b) fval = ui - ival * 1.0;
(c) dval = ui * fval;
(d) cval = ival + fval + dval;

(a)’a‘ 转换成 int,计算后再转换成 char;
(b)ival 转换成 double,ui 转换成 double,计算结果转换成 float 赋值给 fval;
(c)ui 转换成 float,计算结果转换成 double 赋值给 dval;
(d)ival 和 fval 转换成 double,计算结果转换成 char,赋值给 cval。

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;
(b) i = int(*pc);
(c) pv = &d;
(d) pc = (char*)pv;

改写

(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

说明下面表达式的含义。

double slope = static_cast<double>(j / i);

将 j/i 的计算结果强制转换为 double,然后赋值给slope。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值