C++ Primer(第五版)|练习题答案与解析(第四章:表达式)
本博客主要记录C++ Primer(第五版)中的练习题答案与解析。
参考:C++ Primer
C++ Primer
练习题4.1
表达式5+10*20/2的值是多少?
105,P122
练习题4.2
根据4.12节,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加前一致。
(a) *vec.begin()
–> (*vec.begin())
(b) *vec.begin() + 1
--> (*vec.begin() + 1)
练习题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 = -86
(b)-30 + 3 * 21 / 5 = -18
(c)30 / 3 * 21 % 5 = 0
(d)-30 / 3 * 21 % 4 = -2
练习题4.6
写出一个表达式,确定一个整数是奇数还是偶数
int num = 10;
num % 2 == 1 ? cout << "odd" << endl : cout << "even" << endl;
练习题4.7
溢出是何含义,写出三条会溢出的表达式。
溢出,当计算结果超出该类型所能表示的范围就会产生溢出。
表达式:short c = (short)65538; // print c = 2, overflow
练习题4.8
说明在逻辑与、逻辑或及相等运算符中运算对象求值的顺序。
逻辑与、逻辑或运算优先级相同,按照从左到右的顺序求值。相等运算符先求值。
练习题4.9
解释下面的if语句中条件部分的判断过程。
const char *cp = "Hello World";
if (cp && *cp);
// cp是一个地址,不为0。再取*cp的值,为一个字符串,是"Hello World",不为0.最后两者相与,值为True。
练习题4.10
为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止。
代码:
#include <iostream>
using namespace std;
int main()
{
int number = 0;
while (cin >> number)
{
if (42 == number)
{
break;
}
}
}
测试:
1
2
42
练习题4.11
书写一条表达式用于测试4个值a, b, c, d的关系,确保a大于b,b大于c,c大于d。
表达式:if ((a>b) && (b>c) && (c>d))
练习题4.12
假设i,j和k是三个整数,说明表达式i != j < k。
含义:“先取i值,再计算j < k是否成立,成立为大于0的数,否则为0。再计算i != (j < k) 。”
练习题4.13
下述语句中,赋值完i和d的值分别是多少?
语句:int i; double d;
(a)d = i = 3.5;
i = 3, d = 3; i = 3.5, 将3.5隐式转化为整型。
(b)i = d = 3.5;
d = 3.5, i = 3,因为i是整型。
练习题4.14
执行下述if语句会发生什么情况
(a)if (42 = i)
会报错,42是常量,不能赋值。
(b)if (i = 42)
将42赋值给i,if条件为真。
练习题4.15
下面的赋值是非法的,为什么,应该如何修改?
double dval; int ival; int *pi;
dval = ival = pi = 0
pi是地址,不能把指针的值赋值给int(P129)
修改为:dval = ival = *pi = 0;
练习题4.16
尽管下面的语句合法,但它们实际执行的行为可能和预期不一样,为什么?应如何修改?
(a)if (p = getPtr() != 0){}
这是因为赋值运算优先级较低(P130),可能会先判断!=, 再把结果赋值给p,修改为:if ((p = getPtr) != 0)
(b)if (i = 1024)
会把1024赋值给i,再进行判断,改为if (i == 1024)
练习题4.17
说明前置递增运算符和后置递增运算符的区别。
前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回(P132)。
练习题4.18
如果132页那个输出vector对象元素的while循环使用前置递增运算符,会得到什么结果?
会先向后移动一个元素,再输出移动后的值。输出范围为第二个元素到最后一个元素的下一个元素。由于最后一个元素的下一个元素未知,所以会访问到未知内存。
练习题4.19
假设ptr的类型是指向int的指针,vec的类型是vector、ival的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a)ptr != 0 && *ptr++;
判断ptr是否为空,并且ptr指向的值是否为0;
(b)ival ++ && ival;
判断ival及ival++是否为空。运算的顺序为,根据短路求值,先判断ival,再计算++,再进行&&
(c)vec[ival++] <= vec[ival];
运算顺序未知,比如ival = 1,编译器有可能先算vec[ival++],得到ival=2,再计算vec[ival],这样就得不到预期结果(P133)。因此改成vec[ival+1] <= vec[ival];
练习题4.20
假设iter的类型是vector::iterator, 说明下面的表达式是否合法,如果合法,表达式的含义是什么?如果不合法,错在何处?
(a)*iter++;
合法,先对iter+1,再返回iter初始值的副本,再对该副本进行解引用
(b)(*iter)++;
不合法,不能对string类型进行++操作。
(c)*iter.empty();
不合法,不能对指针指向的值判空。可改为(*iter).empty();
(d)iter->empty();
合法
(e)++*iter;
不合法,先求*iter,在进行++操作,不能对string类型做++操作,可改为 *(++iter);
(f)iter++->empty();
合法,++和->的优先级一样,所以遵循自左向右结合,先算iter++的值,返回iter初始值的副本,再进行empty()判断。iter->empty等价于(*iter).empty
练习题4.21
编写程序,使用条件运算符从vector中找到哪些元素是奇数,然后将奇数翻倍。
代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
int main()
{
vector<int> ivec = {1, 2, 3, 4, 5, 6};
for (auto &iter : ivec) {
iter = (iter % 2 == 1) ? iter * 2 : iter;
cout << iter << " ";
}
cout << endl;
}
输出:
2 2 6 4 10 6
练习题4.22
编写程序,将成绩从high pass, pass, fail扩充一种:60-75为low pass,使用两种方法:1.使用条件运算符。2.使用if-else。
代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
int main()
{
int grade;
string finalgrade;
while (cin >> grade)
{
// approach 1
finalgrade = (grade > 90) ? "high pass" : ((grade < 60) ? "fail" : ((grade < 75) ? "low pass" : "pass"));
// approch 2
if (grade > 90)
{
finalgrade = "hign pass";
}
else if (grade < 60)
{
finalgrade = "fail";
}
else if (grade < 75)
{
finalgrade = "low pass";
}
else
{
finalgrade = "pass";
}
cout << finalgrade << endl;
}
}
练习题4.23
指出下面表达式的问题,并说明如何修改。
string s = "word";
string p1 = s + s[s.size()-1] == 's' ? "" : "s";
条件语句优先级较低(P135),要给其加括号。string p1 = s + (s[s.size()-1] == 's' ? "" : "s");
练习题4.25
如果一台机器上int占32位,char占8位,用的是Latin-1字符集,其中‘q’ 的二进制形式是01110001,那么表达式’q’ << 6的值是什么?
“'q' << 6
运算会把结果隐式转化为int类型,因为6是int类型。因此结果的二进制码为:0000 0000 0000 0000 0001 1100 0100 0000,转化为10进制为7232.”
练习题4.26
本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况。
“在某些系统上,int占16为,因此如果用unsigned int来存储的话,在这些机器上就会发生位数不够用的情况。而unsigned long则可以保证在任何机器上都至少有32位。”
练习题4.27
下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7;
(a)cout << (ul1 & ul2) << endl;
0011 & 0111 = 0011 = 3
(b)cout << (ul1 | ul2) << endl;
0011 | 0111 = 0111 = 7
(c)cout << (ul1 && ul2) << endl;
3 && 7 = 1
(d)cout << (ul1 || ul2) << endl;
3 || 7 = 1
练习题4.28
编写一段程序,输出每一种内置类型所占空间的大小。
代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
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;
}
输出:
oid: nullptr_t 4 bytes
bool: 1 bytes
char: 1 bytes
wchar_t: 2 bytes
char16_t: 2 bytes
char32_t: 4 bytes
short: 2 bytes
int: 4 bytes
long: 4 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
long double: 12 bytes
int8_t: 1 bytes
uint8_t: 1 bytes
int16_t: 2 bytes
uint16_t: 2 bytes
int32_t: 4 bytes
uint32_t: 4 bytes
int64_t: 8 bytes
uint64_t: 8 bytes
练习题4.29
推断下面代码的输出,说明原因。
代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
int main()
{
int x[10];
int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
// 10, sizeof(x) = 10*4, sizeof(*x) = 4;
cout << sizeof(p) / sizeof (*p) << endl;
cout << sizeof(p) << endl;
cout << sizeof (*p) << endl;
// sizeof(p)的含义:p是一个int *类型,因此得出的大小应该是指针的大小。
// sizeof(*p)的含义:*p已经对p解引用了,*p实际就是int类型,因此sizeof(*p)得到的是一个int型的大小。
}
输出:
10
1
4
4
练习题4.30
在下面的表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同。
(a)sizeof x + y;
sizeof(x) + y;
(b)sizeof p->mem[i];
sizeof (p->mem[i]);
(c)sizeof a < b;
sizeof (a) < b;
(d)sizeof f();
sizeof (f());
练习题4.31
使用递增和递减运算符时,为什么要用前置版本而不用后置版本,要想使用后置版本需要做哪些改动?
“在for循环中使用前置版本和后置版本都能得到相同的结果。这里使用前置版本的原因,就是4.5节中的建议所述:“前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容,如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。”
代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
int main()
{
vector<int> ivec(10, 0);
vector<int>::size_type cnt = ivec.size();
for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt++)
{
cout << "ix = " << ix << " cnt = " << cnt << endl;
ivec[ix] = cnt;
}
}
练习题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)
{
/* ... */
}
“循环遍历ia数组。ix和ptr的作用相同,一个使用下标遍历,一个使用指针遍历。”
练习题4.33
说明下面表达式的含义。
表达式:someValue ? ++x, ++y : --x, --y;
“因为逗号表达式的优先级最低,按照预期,如果someValue为真,冒号后面的语句不会再执行了,但实际上,编译器会认为冒号后面的--x
属于三目运算符中的语句,而--y
属于一个单独的语句。也就是( someValue ? ++x, ++y : --x), --y;
因此,如果someValue为真,则执行++x,++y,--y,
最后得到的结果是y本身。如果someValue为假,则执行--x,--y,
最终的结果是–y的值。”
练习题4.34
说明下面的表达式中将会发生什么样的类型转化。
(a)if (fval)
float类型转化为bool类型
(b)dval = fval + ival;
int先转化为float,然后赋值给dval时再转化为double类型
(c)dval + ival * cval;
char先转化为int,然后int转化为double
练习题4.35
假设有如下定义,则下面表达式中是否发生了隐式转化?
char cval;
int ival;
unsigned int ui;
float fval;
double dval;
(a)cval = 'a' + 3;
‘a’先转化为int进行计算,得到的结果再转化为char
(b)fval = ui - ival * 1.0;
ival先转化为double,ui也转化为double,double再截为float
(c)dval = ui * fval;
unsigned int先提升为float,float再转为double
(d)cval = ival + fval + dval;
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;
改为:pv = const_cast<string *>(ps);
去除底层const属性。
(b)i = int(*pc);
改为: i = static_cast<int>(*pc);
(c)pv = &d;
改为: pv = static_cast<void *>(&d);
(d)pc = (char *)pv;
改为:pc = reinterpret_cast<char *> (pv);
练习题4.38
说明下面这条表达式的含义。
表达式:double slope = static_cast<double>(j/i);
含义:将j/i的结果转化为double类型,再赋值给slope。