《C++ Primer》第五版习题个人答案
练习4.1
表达式 5 + 10 * 20 / 2 的求值结果是多少?
105
练习4.2
根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
- (a) *vec.begin()
- (b) *vec.begin() + 1
(a) (*vec.begin())
(b) (*vec.begin()) + 1
练习4.3
C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
可以接受,C++ 语言没有明确规定大多数二元运算符的求值顺序,以便为编译器优化提供更多机会。这种策略确实在代码生成效率和潜在编程缺陷之间进行了权衡。
练习4.4
在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
1 | 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2 |
((12/3)4)+(515)+((24%4)/2)
91
练习4.5
写出下列表达式的求值结果。
1 | -30 * 3 + 21 / 5 // (-30*3)+(21/5)=-90+4=-86 |
练习4.6
写出一条表达式用于确定一个整数是奇数还是偶数。
1 | if(i % 2 == 0) |
练习4.7
溢出是何含义?写出三条将导致溢出的表达式。
当计算的结果超出该类型所能表示的范围就会产生溢出。
1 | int maxInt = INT_MAX; |
练习4.8
说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。
- 逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。
- 对于逻辑与运算符来说,当且仅当左侧运算对象为真时才对右侧运算对象求值。
- 对于逻辑或运算符来说,当且仅当左侧运算对象为假时才对右侧运算对象求值。
- 相等性运算符未定义求值顺序,可能导致结果无法预测。
练习4.9
解释在下面的if语句中条件部分的判断过程。
1 | const char *cp = "Hello World"; |
首先判断cp
指针是否为空,才能引用cp
的值。cp
指向字符串"Hello World"
,第一个字符H
不为0,不是空指针,所以为真。
练习4.10
为while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。
1 | int i; |
练习4.11
书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。
1 | if(a > b && b > c && c > d) |
练习4.12
假设i、j 和k 是三个整数,说明表达式 i != j < k 的含义。
<
运算符的优先级高于!=
运算符,所以先判断j < k
返回 1 或 0 ,再判断 i 是否与 bool 值相等,最终结果仍然为 bool 值。
练习4.13
在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
1 | int i; double d; |
练习4.14
执行下述 if 语句后将发生什么情况?
1 | if (42 = i) // `if (42 = i)` 将会导致编译错误,因为字面值常量不能被赋值 |
练习4.15
下面的赋值是非法的,为什么?应该如何修改?
1 | double dval; int ival; int *pi; |
pi
是指针,不能进行赋值。
1 | double dval; int ival; int *pi; |
练习4.16
尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
1 | if (p = getPtr() != 0) |
getPtr() != 0
会先执行,返回一个布尔值(true
或 false
)。然后将这个布尔值赋给 p
,这意味着 p
最终会变成 true
或 false
,而不是 getPtr()
的返回值。
这里将 1024
赋值给 i
,并且由于赋值表达式的值是 1024
,因此条件始终为 true
,因为 1024
是一个非零值。
1 | if (p = (getPtr()) != 0) |
练习4.17
说明前置递增运算符和后置递增运算符的区别。
前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。
练习4.18
如果132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?
第一次解引用是第二个地址,跳过了第一个地址,且最后对v.end()
取值。
练习4.19
假设 ptr 的类型是指向 int 的指针、vec 的类型是vector
、ival 的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
1 | (a) ptr != 0 && *ptr++ |
- (a)
- (b)
- (c)
练习4.20
假设 iter 的类型是 vector
::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
1 | (a) *iter++; |
- (a)
- (b)
- (c)
- (d)
- (e)
- (f)
练习4.21
编写一段程序,使用条件运算符从 vector
中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
练习4.22
本节的示例程序将成绩划分为high pass、pass 和 fial 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?
练习4.23
因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?
1 | string s = "word"; |
练习4.24
本节的示例程序将成绩划分为 high pass、pass、和fail三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?
练习4.25
如果一台机器上 int 占 32 位、char 占8位,用的是 Latin-1 字符集,其中字符’q’ 的二进制形式是 01110001,那么表达式’q’ << 6的值是什么?
练习4.26
在本节关于测验成绩的例子中,如果使用unsigned int 作为quiz1 的类型会发生什么情况?
练习4.27
下列表达式的结果是什么?
1 | unsigned long ul1 = 3, ul2 = 7; |
- (a)
- (b)
- (c)
- (d)
练习4.28
编写一段程序,输出每一种内置类型所占空间的大小。
练习4.29
推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
1 | int x[10]; int *p = x; |
练习4.30
根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。
1 | (a) sizeof x + y |
- (a)
- (b)
- (c)
- (d)
练习4.31
本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
练习4.32
解释下面这个循环的含义。
1 | constexpr int size = 5; |
练习4.34
根据本节给出的变量定义,说明在下面的表达式中奖发生什么样的类型转换:
1 | (a) if (fval) |
- (a)
- (b)
- (c)
练习4.35
假设有如下的定义:
1 | char cval; |
请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。
1 | (a) cval = 'a' + 3; |
- (a)
- (b)
- (c)
- (d)
练习4.36
假设 i 是int类型,d 是double类型,书写表达式 i*=d 使其执行整数类型的乘法而非浮点类型的乘法。
练习4.37
用命名的强制类型转换改写下列旧式的转换语句。
1 | int i; double d; const string *ps; char *pc; void *pv; |
- (a)
- (b)
- (c)
- (d)
练习4.38
说明下面这条表达式的含义。
1 | double slope = static_cast<double>(j/i); |