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。