4.1
【出题思路】
本题旨在考查几种常见算术运算符的优先级和结合律。
【解答】
在算术运算符中,乘法和除法的优先级相同,且均高于加减法的优先级。因此上式的计算结果应该是 105,在编程环境中很容易验证这一点。
4.2
【出题思路】
C++定义了各种各样的运算符,这些运算符根据运算意义的不同被划分成不同的优先级,本题意在让用户先区分出表达式中各个运算符的优先级关系,然后用括号的方式表示哪个运算符先执行。
【解答】
在本题涉及的运算符中,优先级最高的是成员选择运算符和函数调用运算符,其次是解引用运算符,最后是加法运算符。因此添加括号后的等价的式子是:
#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;
}
// 运行结果
系统自动为向量生成一组元素......
生成的向量数据是:
52 51 75 33 32 56 43 55 95 60
验证添加的括号是否正确:
*vec.begin() 的值是:52
*(vec.begin()) 的值是:52
*vec.begin() + 1 的值是:53
(*(vec.begin())) + 1 的值是:53
Process finished with exit code 0
4.3
【出题思路】
本题的关键是考查求值顺序对表达式求值过程的影响。
【解答】
正如题目所说,C++只规定了非常少的二元运算符(逻辑与运算符、逻辑或运算符、逗号运算符)的求值顺序,其他绝大多数二元运算符的求值顺序并没有明确规定。这样做提高了代码的生成效率,但是可能引发潜在的缺陷。
关键是缺陷的风险有多大?我们知道,对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为;而如果运算对象彼此无关,它们既不会改变同一对象的状态也不执行 IO 任务,则函数的调用顺序不受限制。
就作者的观点而言,这样的做法在一定程度上是可以接受的,前提是在编写程序时注意以下两点:
- 拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求;
- 一旦改变了某个运算对象的值,在表达式的其它地方就不要再使用这个运算对象了。
4.4
【出题思路】
本题旨在考查算术运算符的优先级和结合律,乘法和除法的优先级相同,且均高于加减法的优先级;算术运算符都满足左结合律,意味着当优先级相同时按照从左往右的顺序进行结合。
【解答】
添加括号之后的形式应该是 ((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2)
,求值的过程是:首先计算 12 / 3 得到 4,接着 4 * 4 得到 16,同时计算 5 * 15 得到 75,计算 24 % 4 得到 0,接着计算 0 / 2 得到 0,最后执行加法运算 16 + 75 + 0 得到 91。最终的计算结果是 91。
4.5
【出题思路】
本题主要考查乘除法、取余的计算规则。
【解答】
本题用到两条典型的算术运算规则。
一是除法:整数相除结果还是整数,如果商含有小数部分,直接弃除。尤其当除法的两个运算对象的符号不同时,商为负,C++11 新标准规定商一律向 0 取整。
二是取余:如果取余的两个运算对象的符号不同,则负号所在的位置不同运算结果也不相同,m % (-n)
等于 m % n
,(-m) % n
等于 - (m % n)
。
因此,本题的求值结果是:
(a)- 86
(b)- 18
(c)0
(d)- 2
4.6
【出题思路】
根据奇数和偶数的定义可知,能被 2 整除的数是偶数,不能被 2 整除的数是奇数。
【解答】
下面的表达式可以用于确定一个整数是奇数还是偶数,假设该整数名为 num,则表达式 num % 2 == 0
为真时 num 是偶数,该表达式为假时 num 是奇数。
4.7
【出题思路】
当计算的结果超出类型所能表示的范围时,产生溢出。
【解答】
溢出是一种常见的算术运算错误。因为在计算机中存储某种类型的内存空间有限,所以该类型的表示能力(范围)也是有限的,当计算的结果值超出这个范围时,就会产生未定义的数值,这种错误称为溢出。
假设编译器规定 int 占 32 位,则下面的 3 条表达式都将产生溢出错误:
int i = 2147483647 + 1;
int j = -100000 * 300000;
int k = 2019 * 2019 * 2019 * 2019;
编程测试如下所示:
#include <iostream>
using std::cout;
using std::endl;
int main() {
int i = 2147483647 + 1;
int j = -100000 * 300000;
int k = 2019 * 2019 * 2019 * 2019;
cout << "i: " << i << endl;
cout << "j: " << j << endl;
cout << "k: " << k << endl;
}
// 运行结果
i: -2147483648
j: 64771072
k: -509465903
Process finished with exit code 0
显然这与我们的预期不相符,是溢出之后产生的错误结果。
4.8
【出题思路】
逻辑与、逻辑或执行短路求值,相等性运算符则正常计算两个运算对象的值。
【解答】
对于逻辑与运算符来说,当且仅当两个运算对象都为真时结果为真;对于逻辑或运算符来说,只要两个运算对象中的一个为真结果就为真。
逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种策略就是短路求值。其策略是:对于逻辑与运算符来说,当且仅当左侧运算对象为真时才计算右侧运算对象;对于逻辑或运算符来说,当且仅当左侧运算对象为假时才计算右侧运算对象。
值得注意的是,逻辑与运算符和逻辑或运算符是 C++ 中仅有的几个规定了求值顺序的运算符。相等性运算符的两个运算对象都需要求值,C++ 没有规定其求值顺序。
4.9
【出题思路】
本题旨在考查指针和解引用运算符作为 if 条件的用法。
【解答】
cp 是指向字符串的指针,因此上式的条件部分含义是首先检查指针 cp 是否有效。如果 cp 为空指针或无效指针,则条件不满足。如果 cp 有效,即 cp 指向了内存中的某个有效地址,继续解引用指针 cp 并检查 cp 所指对象是否为空字符