???? C++ Primer 0x04 练习题解
更好的阅读体验(实时更新和修正)
4.1 基础
4.1.2 优先级与结合律
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.1.3 求值顺序
4.3 C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
可以接受,给了程序员更多*,C++ 信任程序员,当然这也要求我们有较高的水平了。
4.2算术运算符
4.4 在下面的表达式中添加括号,说明其求值过程及最终结果。编写程序编译该(不加括号的)表达式并输出结果验证之前的推断。
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2)
//16 + 75 + 0
//91
4.5 写出下列表达式的求值结果。
-30 * 3 + 21 / 5 //-86
-30 + 3 * 21 / 5 //-18
30 / 3 * 21 % 5 //0
-30 / 3 * 21 % 4 //-2
4.6 写出一条表达式用于确定一个整数是奇数还是偶数。
4.7 溢出是何含义?写出三条将导致溢出的表达式。
当计算的结果超出该类型所能表示的范围时就会产生溢出。
short short_value = 32767; short_value += 1;
unsigned unsigned_value = 0; unsigned_value -=1;
unsigned short uunsigned_short_svalue = 65535; ++uunsigned_short_svalue;
4.3逻辑和关系运算符
4.8 说明在逻辑与、逻辑或及相等性运算符中运算对象的求值顺序。
- 逻辑与逻辑或求值顺序为短路求值
- 相等运算未定义求值顺序
4.9 解释在下面的 if 语句中条件部分的判断过程。
const char *cp = "Hello World";
if (cp && *cp)
如果cp不是一个空指针,再看*cp,也就是’H’。结果为真
4.10 为 while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。
int x;
while(cin>>x && x!=42){/*...*/}
4.11 书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d。
4.12 假设 i 、j 和 k 是三个整数,说明表达式i != j < k的含义。
首先看j<k,如果j<k返回值为true,表达式变为 i != 1
如果j<k为假返回值为false,表达式变为 i !=0
最终返回的结果还是布尔值
注意 true
和 false
变为 int
的过程
4.4赋值运算符
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
4.14 执行下述 if 语句后将发生什么情况?
if (42 = i) // 赋值运算符的左侧运算对象必须是一个可以修改的左值,编译报错
if (i = 42) // 真
4.15 下面的赋值是非法的,为什么?应该如何修改?
double dval; int ival; int *pi;
dval = ival = pi = 0;
ival = pi
这部分会出错,pi
是指针不能赋值给 ival
double dval; int ival; int *pi;
dval = ival = 0;
pi = 0;
4.16 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
if (p = getPtr() != 0)//赋值运算符优先级低
if (i = 1024)//区分赋值运算符和相等运算符
if ((p = getPtr()) != 0)
if (i == 1024)
4.5 递增和递减运算符
4.17 说明前置递增运算符和后置递增运算符的区别。
前置版本把值+1后直接返回了运算对象(左值);后置版本需要把原始值存下来,以便返回未修改的内容(右值)
4.18 如果132页那个输出 vector 对象元素的 while 循环使用前置递增运算符,将得到什么结果?
从第二个元素开始输出,最终会对 end 解引用,出错
4.19 假设ptr
的类型是指向int
的指针、vec
的类型是vector
、ival
的类型是int
,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a) ptr != 0 && *ptr++ //如果ptr非空,并且ptr指向的值为真,那么ptr指向下一个
(b) ival++ && ival //如果ival为真并且ival+1也为真,那么结果为真
(c) vec[ival++] <= vec[ival] //不正确,没有规定<=两侧的求值顺序
//(c) 修改
// vec[ival] <= vec[ival+1]
4.6 成员访问运算符
4.20 假设iter
的类型是vector<string>::iterator
, 说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?
(a) *iter++;//返回迭代器所指元素后迭代器递增1
(b) (*iter)++;//不合法 string 不支持递增运算符
(c) *iter.empty();//不合法,解引用运算符优先级比点运算符低
(d) iter->empty();//判断迭代器所指元素是否为空
(e) ++*iter;//不合法 string 不支持递增运算符
(f) iter++->empty();//判断迭代器所指元素是否为空然后迭代器递增
4.7 条件运算符
4.21 编写一段程序,使用条件运算符从vector<int>
中找到哪些元素的值是奇数,然后将这些奇数值翻倍。
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
int main(){
vector<int>vec={1,2,3,4,5};
for(auto &v:vec)v=(v&1?2*v:v);
for(auto v:vec)cout << v <<" ";
return 0;
}
4.22 本节的示例程序将成绩划分为high pass
、pass
和 fail
三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为low pass
。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if
语句。哪个版本的程序更容易理解呢?为什么?
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::string;
int main(){
int grade;cin>>grade;
string finalgrade = (grade > 90) ? "hight pass"
: (grade < 60) ? "fail" : "pass";
cout << finalgrade;
return 0;
}
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::string;
int main(){
int grade;cin>>grade;
string finalgrade;
if(grade > 90)finalgrade = "hight pass";
else if(grade >= 60)finalgrade = "pass";
else finalgrade = "fail";
cout << finalgrade;
return 0;
}
第二个版本好,嵌套多了条件运算符可读性降低,if else 更清晰
4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表指出它的问题在哪里?应该如何修改?
string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;
加法优先级高于条件运算符优先级,因此等价于下面的代码
string s = "word";
string pl = (s + s[s.size() - 1]) == 's' ? "" : "s" ;
修改为
string s = "word";
string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;
4.24 本节的示例程序将成绩划分为high pass
、pass
、和fail
三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值的过程将是怎样的?
string finalgrade = (grade > 90) ? "hight pass"
: (grade < 60) ? "fail" : "pass";
如果左结合律那么等价于
string finalgrade = ((grade > 90) ? "hight pass"
: (grade < 60)) ? "fail" : "pass";
4.8 位运算符
4.25 如果一台机器上int
占32位、char
占8位,用的是Latin-1
字符集,其中字符'q'
的二进制形式是01110001
,那么表达式~'q' << 6
的值是什么?
char
先提升成 int
00000000 00000000 00000000 01110001
然后取反
11111111 11111111 11111111 10001110
然后左移动6位
11111111 11111111 11100011 10000000
4.26 在本节关于测验成绩的例子中,如果使用unsigned int
作为quiz1
的类型会发生什么情况?
未定义,有的机器 unsigned int
只有16位
4.27 下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2 //011 3
(b) ul1 | ul2 //111 7
(c) ul1 && ul2//true
(d) ul1 || ul2 //true
ul1 = 011
ul2 = 111
4.9 sizeof 运算符
4.28 编写一段程序,输出每一种内置类型所占空间的大小。
#include<iostream>
using namespace std;
int main(){
cout << "bool size = " << sizeof(bool) <<" bytes" << endl;
cout << "char size = " << sizeof(char) <<" bytes" << endl;
cout << "wchar_t size = " << sizeof(wchar_t) <<" bytes" << endl;
cout << "char16_t size = " << sizeof(char16_t) <<" bytes" << endl;
cout << "char32_t size = " << sizeof(char32_t) <<" bytes" << endl;
cout << "short size = " << sizeof(short) <<" bytes" << endl;
cout << "int size = " << sizeof(int) <<" bytes" << endl;
cout << "long size = " << sizeof(long) <<" bytes" << endl;
cout << "long long size = " << sizeof(long long) <<" bytes" << endl;
cout << "float size = " << sizeof(float) <<" bytes" << endl;
cout << "double size = " << sizeof(double) <<" bytes" << endl;
cout << "long double size = " << sizeof(long double) <<" bytes" << endl;
return 0;
}
4.29 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如不一样,为什么?
int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;//10
//sizeof 不会把数组名字转成指针,sizeof(x)大小为数组内元素大小之和
cout << sizeof(p)/sizeof(*p) << endl;//2
//p 是int* 类型 sizeof(p) = 8
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.10 逗号运算符
4.31 本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。
前置版本把值+1后直接返回了运算对象;后置版本需要把原始值存下来,以便返回未修改的内容
#include <vector>
#include <iostream>
using namespace std;
int main(){
vector<int>ivec(10);
vector<int>::size_type cnt =ivec.size();
for(vector<int>::size_type ix = 0; ix != ivec.size(); ix++, --cnt)
ivec[ix] = cnt;
for(auto v:ivec)cout << v << " ";
return 0;
}
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 并计数
4.33 根据4.12节中的表说明下面这条表达式的含义
someValue ? ++x, ++y : --x, --y
等价
(someValue ? ++x, ++y : --x), --y
如果 somevalue
为真,那么执行 ++x
和++y
,返回y
。然后丢掉y
值,执行--y
,返回y值
如果 somevalue
为假,那么执行 --x
,返回x
。然后丢掉x
值,执行--y
,返回y值
#include <vector>
#include <iostream>
using namespace std;
int main(){
int x=10,xx=10,y=0,yy=0;
bool somevalue = true;
cout << (somevalue ? ++x,++y:--x) << endl;//输出1
cout << ((somevalue ? ++xx,++yy:--xx),--yy) << endl;//输出0
return 0;
}
4.11 类型转换
4.11.1 算术转换
4.34 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换:
(a) if (fval)//转为bool
(b) dval = fval + ival;//ival转为float,最终结果转为double
(c) dval + ival * cval;//cval转为int,ival*cval结果转为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,最终转为float
(c) dval = ui * fval;//ui转为float,最终转为double
(d) cval = ival + fval + dval;//ival 转为float,相加结果转为double,最终结果转为char
4.11.3 显示转换
4.36 假设 i
是int
类型,d
是double
类型,书写表达式 i*=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 = static_cast<char*>(pv);
4.38 说明下面这条表达式的含义。
double slope = static_cast<double>(j/i);
j/i
强制类型转换为double
然后赋值给slope