表达式 5 + 10 * 20 / 2 的求值结果是多少?
5 + 10 * 20 / 2 = 105
根据 4.12 节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
(a)
*vec.begin()
(b)
*vec.begin() + 1
*(vec.begin())
*(vec.begin()) + 1
C++ 语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
可以接受
在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2) = 91
写出下列表达式的求值结果。
(a)
-30 * 3 + 21 / 5
(b)
-30 + 3 * 21 / 5
(c)
30 / 3 * 21 % 5
(d)
-30 / 3 * 21 % 4
-30 * 3 + 21 / 5 = -86
-30 + 3 * 21 / 5 = -18
30 / 3 * 21 % 5 = 0
-30 / 3 * 21 % 4 = -2
写出一条表达式用于确定一个整数是奇数还是偶数。
int ival;
if (ival % 2 == 0) {
cout << ival << "是偶数" << endl;
} else {
cout << ival << "是奇数" << endl;
}
溢出是何含义?写出三条将导致溢出的表达式。
溢出是指计算的结果超出该类型所能表示的范围
short shortVal1 = 32768;
short shortVal2 = 30000 * 2;
short shortVal3 = 32767 + 1;
说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。
- 在逻辑与中,先计算左侧运算对象,若为真,再计算右侧运算对象
- 在逻辑或中,先计算左侧运算对象,若为假,再计算右侧运算对象
- 在相等性运算符中,先计算左侧运算对象,在计算右侧运算对象
解释在下面的if语句中条件部分的判断过程。
const char *cp = "Hello World"; if (cp && *cp)
先检查指针 cp
是否指向一个对象,如果是,则检查 cp
指向的字符数组是否为空,若非空,则条件为真,否则条件为假
为 while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。
int val;
while (cin >> val && val != 42) {
/* ... */
}
书写一条表达式用于测试 4 个值 a、b、c、d 的关系,确保 a 大于 b、b 大于 c、c 大于 d。
if (a > b && b > c && c > d) {
/* ... */
}
假设 i、j 和 k 是三个整数,说明表达式
i != j < k
的含义。
先判断 j
和 k
的大小关系,若 j < k
,则当 i != 1
时表达式为 true
,否则表达式为 false
;若 j >= k
,则当 i != 0
时表达式为 true
,否则表达式为 false
在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
int i; double d;
(a)
d = i = 3.5;
(b)
i = d = 3.5;
i = 3, d = 3
i = 3, d = 3.5
执行下述 if 语句后将发生什么情况?
if (42 = i) // ... if (i = 42) // ...
- 编译无法通过,字面值
42
是右值,无法赋值 - 程序运行,
i
被赋值为 42,if
条件始终为真
下面的赋值是非法的,为什么?应该如何修改?
double dval; int ival; int *pi; dval = ival = pi = 0;
pi
的类型为 int*
,无法转换为 ival
的 int
类型。修改如下
double dval; int ival; int *pi;
dval = ival = 0; pi = 0;
尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
(a)
if (p = getPtr() != 0)
(b)
if (i = 1024)
- 赋值运算的优先级较低,修改为
if ((p = getPtr()) != 0)
- 混淆了赋值运算符和相等运算符,修改为
if (i == 1024)
说明前置递增运算符和后置递增运算符的区别。
前置递增运算符首先将运算对象加 1,然后将改变后的对象作为左值返回;后置递增运算符将运算对象加 1,但是将改变前的运算对象作为右值返回
如果 132 页那个输出 vector 对象元素的 while 循环使用前置递增运算符,将得到什么结果?
无法输出第一个元素,且若 vector
中无负值,将试图解引用一个不存在的元素,造成程序错误
假设 ptr 的类型是指向 int 的指针、vec 的类型是 vector、ival 的类型是 int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a)
ptr != 0 && *ptr++
(b)
ival++ && ival
(c)
vec[ival++] <= vec[ival]
ptr
是非空指针,且ptr
所指对象不为 0 时表达式为真,并将ptr
加 1ival
不为 0 时表达式为真,并将ival
加 1- 表达式为真,将
ival
加 1
假设 iter 的类型是 vector::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
(a)
*iter++;
(b)
(*iter)++;
(c)
*iter.empty();
(d)
iter->empty();
(e)
++*iter;
(f)
iter++->empty();
- 合法,返回
iter
所指元素的引用,iter
指向下一个元素 - 不合法,返回
iter
所指元素的引用,但string
对象不能递增 - 不合法,解引用运算符的优先级低于点运算符,迭代器没有
empty()
成员 - 合法,判断
iter
所指的string
对象是否为空 - 不合法,返回
iter
所指元素的引用,但string
对象不能递增 - 合法,判断
iter
所指的string
对象是否为空,iter
指向下一个元素
编写一段程序,使用条件运算符从 vector<int> 中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
本节的示例程序将成绩划分为 high pass、pass 和 fail 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用 1 个或多个 if 语句。哪个版本的程序更容易理解呢?为什么?
使用 if 语句的版本更容易理解,条件运算符嵌套过多
因为运算符的优先级问题,下面这条表达式无法通过编译。根据 4.12 节中的表(第 147 页)指出它的问题在哪里?应该如何修改?
string s = "word"; string pl = s + s[s.size() - 1] == 's' ? "" : "s";
条件运算符的优先级非常低,原条件表达式判断 s + s[s.size() - 1]
和 's'
是否相等,而 string
对象和字符字面值无法相互比较。修改如下
string s = "word";
string pl = s + ((s[s.size() - 1] == 's') ? "" : "s");
本节的示例程序将成绩划分为 high pass、pass、和 fail 三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值过程将是怎样的?
第一个条件检查成绩是否在 90 分以上,如果是,得到 "high pass"
,执行后面的条件表达式:字符串不为空,条件成立,得到 "fail"
;如果否,执行后面的条件表达式:判断成绩是否在 60 分以下,如果是,得到 "fail"
;否则得到 "pass"
如果一台机器上 int 占 32 位、char 占 8 位,用的是 Latin-1 字符集,其中字符 'q' 的二进制形式是 01110001,那么表达式
'q' << 6
的值是什么?
输出 -7296
,char
类型的运算对象会提升成 int
类型,提升时运算对象原来的位保持不变,往高位添加 0
在本节关于测验成绩的例子中,如果使用 unsigned int 作为 quiz1 的类型会发生什么情况?
在 int
类型为 16 位的机器上会溢出,造成错误结果
下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7;
(a)
ul1 & ul2
(b)
ul1 | ul2
(c)
ul1 && ul2
(d)
ul1 || ul2
ul1 & ul2 = 3
ul1 | ul2 = 7
ul1 && ul2 =1
ul1 || ul2 = 1
编写一段程序,输出每一种内置类型所占空间的大小。
推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?
int x[10]; int *p = x; cout << sizeof(x) / sizeof(*x) << endl; cout << sizeof(p) / sizeof(*p) << endl;
输出
10
2
int*
类型的对象(指针)占 8 个字节,int
类型的对象占 4 个字节
根据 4.12 节中的表(第 147 页),在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。
(a)
sizeof x + y
(b)
sizeof p->mem[i]
(c)
sizeof a < b
(d)
sizeof f()
(sizeof x) + y
sizeof (p->mem[i])
(sizeof a) < b
(sizeof f())
本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
后置递增运算符需要将运算对象的原始值存储下来,将运算对象加 1 后再返回原始值,而此程序无需运算对象的原始值,使用后置递增运算符会造成不必要的浪费。使用后置运算符的程序如下
auto cnt = ivec.size();
for (decltype(ivec.size()) ix = 0; ix != ivec.size(); ix++, cnt--) {
ivec[ix] = cnt;
}
解释下面这个循环的含义。
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
初始值为 0。每次循环迭代 ptr
和 ix
,只要 ix
小于数组大小且指针未指向最后一个元素的下一个位置,就执行循环体,然后将 ix
加 1,ptr
向后移动一个位置
根据 4.12 节中的表(第 147 页)说明下面这条表达式的含义。
someValue ? ++x, ++y : --x, --y
判断 someValue
的值,如果为真(不为 0),则 x
和 y
都递增 1;否则 x
和 y
都递减 1
根据本节给出的变量定义,说明在下面的表达式中奖发生什么样的类型转换:
(a)
if (fval)
(b)
dval = fval + ival;
(c)
dval + ival * cval;
需要注意每种运算符遵循的是左结合律还是右结合律。
fval
转换成bool
ival
转换成float
,然后该float
值转换成double
cval
提升成int
,然后该int
值转换成double
假设有如下的定义:
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'
提升成int
,然后该int
值转换成char
ival
转换成double
,然后ui
转换成double
,然后该double
值转换成float
ui
转换成float
,然后该float
值转换成double
ival
和fval
转换成double
,然后该double
值转换成char
假设 i 是 int 类型,d 是 double 类型,书写表达式
i *= d
使其执行整数类型的乘法而非浮点类型的乘法。
i *= static_cast<int>(d)
用命名的强制类型转换改写下列旧式的转换语句。
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;
pv = static_cast<void *>(const_cast<string *>(ps));
i = static_cast<int>(*pc);
pv = static_cast<void *>(&d);
pc = reinterpret_cast<char *>(pv);
说明下面这条表达式的含义。
double slope = static_cast<double>(j / i);
将 j/i
的结果转换为 double
类型赋给 slope
上一章:第 3 章 字符串、向量和数组
下一章:第 5 章 语句