练习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
在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
(((12/3)4)+(515))+((24%4)/2)
91
练习4.5
写出下列表达式的求值结果。
-30 * 3 + 21 / 5 // -90+4 = -86
-30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18
30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0
-30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2
(a)-86 (b)-18
(c)0 (d)-2
练习4.6
写出一条表达式用于确定一个整数是奇数还是偶数。
i % 2 == 0 ? “even” : “odd”
练习4.7
溢出是何含义?写出三条将导致溢出的表达式。
short svalue = 32767; ++svalue; // -32768
unsigned uivalue = 0; --uivalue; // 4294967295
unsigned short usvalue = 65535; ++usvalue; // 0
练习4.8
说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。
逻辑与、逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。
逻辑与:当且仅当左侧运算对象为真时才对右侧运算对象求值。
逻辑或:当且仅当左侧运算对象为假时才对右侧运算对象求值。
相等性运算符:求值顺序不明确。
练习4.9
解释在下面的if语句中条件部分的判断过程。
const char *cp = "Hello World";
if (cp && *cp)
当指针cp不为空时,才判断解引用cp的值。
我们知道,cp不为空,&&左侧为true;*cp为’H’,右侧也为真,所以if语句为真。
练习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 的含义。
根据4.12运算符优先级表我们得到<的优先级大于!=,所以j < k得到bool值,bool值再与i比较是否不等。等价于:i != (j < k)
练习4.13
在下述语句中,当赋值完成后 i 和 d 的值分别是多少?
int i; double d;
d = i = 3.5; // i = 3, d = 3.0
i = d = 3.5; // d = 3.5, i = 3
(a)i=3,d=3 (b)i=3,d=3.5
练习4.14
执行下述 if 语句后将发生什么情况?
if (42 = i) // 编译错误。赋值运算符左侧必须是一个可修改的左值。而字面值是右值。
if (i = 42) // true.
非法操作;
if判断为真。
练习4.15
下面的赋值是非法的,为什么?应该如何修改?
double dval; int ival; int *pi;
dval = ival = pi = 0;
pi改为*pi
练习4.16
尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
if (p = getPtr() != 0)
if (i = 1024)
(a)if((p = getPtr()) != 0);(b)if(i == 1024)。
练习4.17
说明前置递增运算符和后置递增运算符的区别。
前置递增运算符:将运算对象加1,然后将改变后的对象作为求值结果;
后置递增运算符:将运算对象加1,但是求值结果是运算对象改变之前那个值的副本。
练习4.18
如果132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?
循环第一次解引用第二个地址;循环到最后一次时,将会解引用vector最后一个地址的后一个位置(等同于v.end())
练习4.19
假设 ptr 的类型是指向 int 的指针、vec 的类型是vector、ival 的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]
(a)指针ptr不为空时,判断指针所指的值是不是不为0;
(b)判断ival和ival+1是不是不为0;
(c)vec[ival] <= vec[ival+1]。
练习4.20
假设 iter 的类型是 vector::iterator, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
(a) *iter++;
(b) (*iter)++;
(c) *iter.empty();
(d) iter->empty();
(e) ++*iter;
(f) iter++->empty();
(a)合法,求iter的解引用,并把iter指向下一个位置;
(b)不合法,*iter为字符串,不能++;
(c)不合法,iter没有empty()成员;
(d)合法,iter所指向的值是否为空;
(e)不合法,字符串不能++;
(f)合法,先判断是否为空,再iter++。
练习4.21
编写一段程序,使用条件运算符从 vector 中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> iv{1,2,3};
for(auto &i:iv)
i = (i % 2) ? (i * 2):i;
for(const auto &i:iv)
cout << i << " ";
cout << endl;
}
练习4.22
本节的示例程序将成绩划分为high pass、pass 和 fial 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?
一个if语句更容易理解,逻辑更清晰。
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main()
{
int grade = 59;
string finalgrade;
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;
}
练习4.23
因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?
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”;
if grade > 90,finalgrade = “high pass” ? “fail” : “pass”;
finalgrade = “fail”;
grade <= 90时,结果符合预期。
练习4.25
如果一台机器上 int 占 32 位、char 占8位,用的是 Latin-1 字符集,其中字符’q’ 的二进制形式是 01110001,那么表达式~‘q’ << 6的值是什么?
未定义的行为。(~优先于<<,执行~时,会先将char转换为int,接着按位求反~后,得到一个负值。如果运算对象是带符号的且它的值为负,那么运算符如何处理运算对象的“符号位”依赖于机器。)
练习4.26
在本节关于测验成绩的例子中,如果使用unsigned int 作为quiz1 的类型会发生什么情况?
int最小为16位,可能会出现位数不够的情况。
练习4.27
下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2
(b) ul1 | ul2
(c) ul1 && ul2
(d) ul1 || ul2
011
111
(a)011,3
(b)111,7
(c)true
(d)true
练习4.28
编写一段程序,输出每一种内置类型所占空间的大小。
#include <iostream> // high level input/output operations.
int main()
{
// by using method below only include what is needed.
using std::cout;
using std::endl;
// void type
cout << "void: nullptr_t\t" << sizeof(std::nullptr_t) << " bytes" << endl << endl;
// boolean type
cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl << endl;
// charactor type
cout << "char:\t\t" << sizeof(char) << " bytes" << endl;
cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl;
cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl;
cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl << endl;
// integers type
cout << "short:\t\t" << sizeof(short) << " bytes" << endl;
cout << "int:\t\t" << sizeof(int) << " bytes" << endl;
cout << "long:\t\t" << sizeof(long) << " bytes" << endl;
cout << "long long:\t" << sizeof(long long) << " bytes" << endl << endl;
// floating point type
cout << "float:\t\t" << sizeof(float) << " bytes" << endl;
cout << "double:\t\t" << sizeof(double) << " bytes" << endl;
cout << "long double:\t" << sizeof(long double) << " bytes" << endl << endl;
// Fixed width integers
cout << "int8_t:\t\t" << sizeof(int8_t) << " bytes" << endl;
cout << "uint8_t:\t" << sizeof(uint8_t) << " bytes" << endl;
cout << "int16_t:\t" << sizeof(int16_t) << " bytes" << endl;
cout << "uint16_t:\t" << sizeof(uint16_t) << " bytes" << endl;
cout << "int32_t:\t" << sizeof(int32_t) << " bytes" << endl;
cout << "uint32_t:\t" << sizeof(uint32_t) << " bytes" << endl;
cout << "int64_t:\t" << sizeof(int64_t) << " bytes" << endl;
cout << "uint64_t:\t" << sizeof(uint64_t) << " bytes" << endl;
return 0;
}
练习4.29
推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(p)/sizeof(*p) << endl;
40/4=10,数组所占的字节数/数组类型int所占的字节数,就是数组的个数;
4/4=1,指针所占的字节数/int所占的字节数。
练习4.30
根据4.12节中的表,在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。
(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.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的功能都是为了遍历数组,功能一样,实现的过程不一样,ptr是指针方式实现,ix是索引方式实现。
练习4.33
根据4.12节中的表说明下面这条表达式的含义。
someValue ? ++x, ++y : --x, --y
(someValue ? ++x, ++y : --x), --y
练习4.34
根据本节给出的变量定义,说明在下面的表达式中奖发生什么样的类型转换:
(a) if (fval)
(b) dval = fval + ival;
(c) dval + ival * cval;
需要注意每种运算符遵循的是左结合律还是右结合律。
(a)float变换为bool;
(b)ival变换为float,结果变换为double;
(c)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,int->char;
(b)ival->double,ui->double,double->float;
(c)ui->float,float->double;
(d)ival->float,float->double,double->char。
练习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 = reinterpret_cast<char*>(pv);
练习4.38
说明下面这条表达式的含义。
double slope = static_cast<double>(j/i);
将(j/i)转换为double,并赋值给slope。