C++ Primer 第五版 课后章节练习答案 第四章

编译软件为 vs2015。

第四章

练习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;

测试程序:

#include "stdafx.h"
#include<iostream>
#include<vector>
using std::vector;
using std::cout;
using std::endl;


int main() {
	vector<int>vec = { 5,4,3,2,1 };
	int a = *vec.begin();
	int b = *vec.begin() + 1;
	int aa = (*vec.begin());//输出的是 vec[0]
	int bb = *(vec.begin()) + 1;//输出的是 vec[0]+1
	int c = *(vec.begin() + 1);
	cout << a << " " << b << endl;
	cout << aa << " " << bb << endl;
	cout << c;//注意 c 方式的括号输出的是 vec[1]
	cout << endl;
	return 0;
}

练习4.3:

C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。

解答:

我认为可以接受,c++中只要程序员避开程序中未定义的行为,避免出现此类错误,就可以提高代码的效率。 如果规定二元运算符的求值顺序,不仅会使代码效率降低,也会使得程序更加繁琐。

练习4.4:

在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出其结果验证之前的推断。

12 / 3 * 4 + 5 * 15 + 24 % 4 / 2

解答:

(( 12 / 3 )* 4) + ( 5 * 15 )+(( 24 % 4 )/ 2)= 91

求值过程,先计算 (( 12 / 3 )* 4),为 16,在加上 ( 5 * 15 )= 75,结果是 91,然后计算 (( 24 % 4 )/ 2),先求 24 % 4,余数为 0,0 / 2 = 0,91 + 0 = 91,最终结果为 91。

验证程序:

#include "stdafx.h"
#include<iostream>
using std::endl;
using std::cout;

int main()
{
	int a = 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2;
	cout << a << endl;
    return 0;
}

练习4.5:

写出下列表达式的求值结果:

(a)- 30 * 3 + 21 / 5

(b)- 30 + 3* 21 / 5

(c)30 / 3 * 21 % 5

(d)- 30 / 3 * 21 % 4

解答:

(a)- 86

(b)- 18

(c)0 (根据左结合律,按照从左到右的顺利计算)

(d)- 2 (注意此情况负号要保留)

练习4.6:

写出一条表达式确定一个整数是奇数还是偶数。

解答: 

i % 2 == 0 ? "even" : "odd"

 等价程序:

#include "stdafx.h"
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
	/* //if - else 语句的形式
       int a = 0;
	cout << " please imput a number: " << endl;
	cin >> a;
	if (a % 2 == 0)
		cout << " it is an even number! " << endl;//被 2 整除为偶数
	else
		cout << " it is an odd number! " << endl;//否则为奇数
		*/
	int i;
	cout << " please input a number:" << endl;
	cin >> i;
	string s = i % 2 == 0 ? "even" : "odd";
	cout << s << endl;
	return 0;
}

练习4.7:

溢出是何含义?写出三条将导致溢出的表达式。

解答:

溢出就是当计算的结果超出该类型所能表示的范围时会产生溢出。

(1) short val = 32767;val += 1;cout << val << endl;// val 输出 - 32768 

short 类型占 16 位, 能表示的最大值为 32767,当给它加一时超出范围无法表示,其结果处理方式也只是将符号位从 0 变成了 1 得到一个负值。溢出情况在不同的系统中还可能有其他结果甚至导致程序崩溃。

(2)unsigned long u = -1; cout << u << endl;// u 输出 4294967295

unsigned long 表示范围 0 ~  4294967295,将 u 赋值一个负数,其结果处理将输出所能表示的最大值。

(3)unsigned short i = 0;i -= 1;cout << i << endl;//与上述同理,i 输出值 65535

练习4.8:

说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。

解答: 

逻辑与:只有在 "左侧" 的运算对象为 "真" 时才会继续对右侧的对象求值。

逻辑或:只有在 "左侧" 的运算对象为 "假" 时才会继续对右侧的对象求值。

相等性运算符: 只有两侧的运算对象同时为真时,返回结果为真。

 练习4.9:

解释在下面的 if 语句中条件部分的判断过程。

const char *cp = "Hello World";

if (cp && *cp);

解答:

if 语句里逻辑与两侧都 "非零" 时为真,cp 为一个指向字符常量的指针,  存放的是字符的地址,非零,为真。*cp 指向字符的首字母,“ H ”,非零,为真。

练习4.10:

为 while 循环写一个条件,使其从标准输入中读取整数,遇到 42 时停止。

解答:

int a = 0;

while (cin >> a && a != 42)

程序:

#include "stdafx.h"
#include<iostream>
using std::cin;
using std::cout;
using std::endl;

int main()
{
	int a = 0;
	cout << " please input your number:" << endl;
	while (cin >> a && a != 42)
		cout << a << endl;
    return 0;
}

练习4.11:

书写一条表达式用于测试 4 个值 a、b、c、d 的关系,确保 a 大于 b、b 大于 c、c 大于 d。

解答: 

if(a>b && b>c && c>d)

程序:

#include "stdafx.h"
#include<iostream>
using std::cin;
using std::cout;
using std::endl;

int main()
{
	int a, b, c, d;
	cout << " please input 4 numbers: " << endl;
	cin >> a >> b >> c >> d;
	if (a > b && b > c && c > d)
		cout << " true! " << endl;
	else
		cout << " false." << endl;
    return 0;
}

练习4.12:

假设 i,j 和 k 是三个整数,说明表达式 i != j < k 的含义。

解答:

表达式的含义是将 i 与 j < K的布尔返回值进行比较。即 判断 i 是否等于 0 或 1,当 j < k 时,返回 1,判断 i 不等于 1,则表达式为真,j >= k 时,返回 0,此时 i 不等于 0,表达式为真。

练习4.13:

在下述语句中,当赋值完成后 i 和 d 的值分别是多少?

int i; double d;

(a) d = i = 3.5;

(b) i = d = 3.5;

解答:

(a)d = 3.0, i =  3

(b)i = 3,d = 3.5

练习4.14:

执行下述 if 语句后将发生什么情况?

if ( 42 = i)// ......

if (i = 42)// ......

解答:

第一条为非法的赋值语句,字面值为右值,不能修改,不可进行赋值操作。

第二条语句是判断赋值的结果是否为真,i 不为零,条件为真。 

练习4.15:

下面的赋值是非法的,为什么?应该如何修改?

double dval;int ival;int *pi;

dval = ival = pi = 0;

解答: 

赋值语句非法因为不能将指针赋值给整型,double 型和 int 可以转换。

修改:dval = ival = 0;pi = 0;

练习4.16:

尽管下面的语句合法。但他们实际执行的行为可能和预期并不一样,为什么?

应该如何修改?

(a)if(p = getptr( ) ! = 0)

(b)if (i = 1024)  

解答: 

(a)赋值运算符的优先级较低,需要加括号。修改:if ((p = getptr( )) != 0)

(b)赋值运算符容易和相等运算符混淆。修改:if (i == 42)

练习4.17:

说明前置递增运算符和后置递增运算符的区别。

解答:

 前置版本的运算符将运算对象加 1 或减 1,然后将 ''改变后的对象'' 作为求值结果。后置版本也会将运算对象加 1 或减 1,但是求值结果是运算对象 ''改变之前'' 的那个值的副本。

练习4.18:

如果第 132 页那个输出 vector 对象元素的 while 循环使用前置递增运算符,将得到什么结果? 

解答: 

 若使用前置递增运算符返回的是改变后的结果,其输出会从第二个元素开始,并且会指向尾后迭代器,是一个并不实际存在的元素,将会出现错误。

练习4.19:

假设 ptr 的类型是指向 int 的指针、vec 的类型是 vector<int>、ival 的类型是 int,说明下面的表达式是何含义?如果表达式不正确,为什么?应该如何修改?

(a)ptr != 0 && *ptr++

(b)ival++ && ival

(c)vec[ ival++ ] <= vec[ ival ]

解答:

(a)检查指针 ptr 不等于 0 ,同时 ptr 依次向前移动一个元素。

(b)检查 ival+1 和 ival 是否同时为 0 ,输出真。 

(c)未定义的行为。无法判断右侧的 ival 是递增前还是递增后的值,可以修改为 vec[ ival ] <= vec[ ival + 1 ]。

练习4.20:

假设 iter 的类型是 vector<string>::iterator,说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法,错在何处?

(a)*iter++

(b)(*iter)++

(c)*iter.empty

(d)iter->empty

(e)++*iter

(f)iter++->empty

解答: 

(a)合法。含义是返回 iter 所指元素并将 iter 移动到下一位。

(b)不合法。含义是对 iter 所指的元素做后置递增运算,但是 iter 元素类型是字符串,无法递增。

(c)不合法。iter 是指针,不存在 empty 成员,其含义是判断 iter 所指对象是否为空,正确写法是 (*iter).empty。

(d)合法。含义是判断 iter 所指的对象是否为空。等价于 (*iter).empty。

(e)不合法。其含义是对 iter 所指的元素做前置递增运算,字符串无法递增。

(f)合法。含义是判断 iter 所指对象是否为空,并将其移动到下一位置。

练习4.21:

编写一段程序,使用条件运算符从 vector<int> 中找到哪些元素的值是奇数,然后将这些奇数翻倍。

解答:

#include "stdafx.h"
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;

int main()
{
	vector<int> vec = { 1,2,3,4,5,15,17,20 };
	for (auto &i : vec) {
		i = (i % 2 ) ? i * 2 : i;//除以 2 若余数为 1 此值翻倍,余数为 0 则不变
		cout << i << endl;
	}
    return 0;
}

 练习4.22:

本届的示例程序将成绩划分为 high pass、pass 和 fail 三种,扩展该程序使其进一步将 60 分到 75 分之间的成绩设定为 low pass。要求程序包含两个版本:一个版本只使用条件运算符;另一个版本只使用 1 个或多个 if 语句。哪个版本的程序更容易理解呢?为什么?

解答:

#include "stdafx.h"
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
using std::string;

int main()
{
	//只使用条件运算符版本
	int grade = 0;
	cout << " please input the grade: " << endl;
	while (cin >> grade) {
		string finalgrade = (grade > 90) ? "high pass"
			: (grade < 60) ? "fail"
			: (grade >= 75) ? "pass" : "low pass";
		cout << finalgrade << endl;
	}
    return 0;
}

 

#include "stdafx.h"
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
using std::string;

int main()
{
	int grade = 0;
	cout << " please input the grade: " << endl;
	//只使用 if 语句
	while (cin >> grade) {
		if (grade > 90)
			cout << " high pass " << endl;
		if (grade >= 75)
			cout << " pass " << endl;
		if (60 < grade < 75)
			cout << " low pass " << endl;
		if (grade < 60)
			cout << " fail " << endl;
	}*/
    return 0;
}

 练习4.23:

因为运算符的优先级问题,下面这条表达式无法通过编译。根据 4.12 节中的表(第 147 页)指出它的问题在哪里?应该如何修改?

string s = "word";

string pl = s + s[ s.size( ) -1] == 's' ? '''' :"s";

解答:

 根据表可以知道加法的优先级高于相等判断符,所以原程序中执行的是 s 和自己相加后的结果在执行条件运算符。

修改方法:string pl = s + (s[ s.size( ) -1] == 's' ? '''' :"s");

练习4.24:

本节的示例程序将成绩划分为 high pass、pass 和 fail 三种,它的依据是条件运算符满足右结合律。假如条件运算符满足的是左结合律,求值过程是怎样的?

解答:

满足左结合律,其运算过程相当于

finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";

含义是若 grade > 90 为假,返回(grade<60)的结果,再根据此结果判断是 “fail” 还是“pass”,若满足左结合律则可能不能得到预期的结果。

练习4.25:

如果过一台机器上 int 占 32 位,char 占 8 位,用的是 Latin-1 字符集,其中字符 'q' 的二进制形式是 01110001 ,那么表达式 ~'q'<<6 的值是什么。

解答:

 根据优先级,'q' 先进行取反操作,在左移位。

'q' :01110001

取反前会先进行提升为32位,提升后取反结果:11111111 11111111 11111111 10001110

然后左移 6 位:11111111 11111111 11100011 10000000

练习4.26:

在本节关于测验成绩的例子中,如果使用 unsigned int 作为 quiz1 的类型会发生什么情况?

解答:

 unsigned int 只能含有16 位,无法定义第 27 位的值,将会导致第 27 位的数据丢失。

练习4.27:

下列表达式的结果是什么?

unsigned long ul1 = 3,ul2 = 7;

(a)ul1 & ul2

(b)ul1 | ul2

(c)ul1 && ul2

(d)ul1 || ul2

解答:

unsigned long 为 32 位,所以 ul1 和 ul2 的二进制表达分别:007

3:00000000 00000000 00000000 00000011

7:00000000 00000000 00000000 00000111

因此:

(a)3

(b)7

(c)ul1 = 3、ul2 = 7同时不为零,输出为真。

(d)ul1 = 3 不为零,输出真。

练习4.28:

编写一段程序,输出每一种内置类型所占空间的大小。

解答:

#include "stdafx.h"
#include<iostream>
using std::cout;
using std::endl;

int main()
{
	cout << " bool: " << sizeof(bool) << endl;
	cout << " char: " << sizeof(char) << endl;
	cout << " wchat_t: " << sizeof(wchar_t) << endl;
	cout << " char16_t: " << sizeof(char16_t) << endl;
	cout << " char32_t: " << sizeof(char32_t) << endl;
	cout << " short: " << sizeof(short) << endl;
	cout << " int: " << sizeof(int) << endl;
	cout << " long: " << sizeof(long) << endl;
	cout << " long long: " << sizeof(long long) << endl;
	cout << " float: " << sizeof(float) << endl;
	cout << " double: " << sizeof(double) << endl;
	cout << " long double: " << sizeof(long double) << endl;
    return 0;
}

练习4.29:

推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?

int x[10]; int *p = x;
cout << sizeof(x) / sizeof(*x) << endl;
cout << sizeof(p) / sizeof(*p) << endl;

解答:

第一个输出为 10,第二个输出为 1。(若为 64 位机则输出 2,可以用程序检验一下指针本身所占空间大小)

练习4.30:

根据 4.12 节中的表(第 147 页),在下述表达式的适当位置加上括号,使得加上括号之后的表达式的含义与原来的含义相同。

(a)sizeof x + y

(b)sizeof p->mem[i]

(c)sizeof a < b

(d)sizeof f ( )

解答:

(a)(sizeof x) + y ;当 + 用作加法运算符时,其优先级低于 sizeof

(b)sizeof (p->mem[i])

(c)(sizeof a) < b

(d)sizeof (f( ))

练习4.31:

本节的程序使用了前置版本的递增运算符和递减运算符,解释为什么要用前置版本而不用后置版本。要想使用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序。

解答:

因为前置版本直接返回的递增或递减之后的值,后置版本需要存储原始值且其返回结果是未改变的值。通常使用前置版本会避免不必要的工作,若果不需要修改前的值,后置版本只会增加程序的负担。

修改方法:直接修改为 ix ++,cnt -- 即可。

重写程序并验证:

#include "stdafx.h"
#include<iostream>
#include<vector>
using std::vector;
using std::cout;
using std::endl;


int main()
{
	vector<int>ivec = { 0,0,0,0,0,0 };
	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 << endl;
    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 ) { /*......*/}

解答:

定义一个指针 ptr 指向 ia 首元素,ix 为零。ix 依次递增直到等于数组的下标大小(不包括)结束,指针指向的元素一次移动到下一个位置,直到指向数组的尾迭代器(不包括)为止,循环遍历整个数组。

练习4.33:

根据 4.12 节中的表(第 147 页)说明下面这条表达式的含义。

someValue ? ++x,++y:--x,--y

解答:

根据表可知运算符 '' ?:''的优先级高于 '' ,''因此原表达式相当于

(someValue ? ++x,++y:--x),--y

当 someValue 为真时,执行的是 (++x,++y ),即 x 递增, y 递增又递减;当 someValue 为假时,执行(--x),即 x 递减,?:语句结束后,在执行 --y ,可知整个表达式最后返回的是 --y 的值。

练习4.34:

根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换: 

(a)if (faval)

(b)dval = fval + ival

(c)dval + ival * cval

需要注意每种运算符遵循的是左结合律还是右结合律。

解答:

 (a)fval 将转换成 bool 类型

(b)ival 先转换成 float 型与 fval 相加,其结果在转换成 double 型

(c)cval 先转换成 int 型与 ival 相乘,其相乘后的结果在转换成 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 型,表达式的结果在转换成 char 型。

(b) ival 转换为 double 型,ui 转换为 double 型,右侧的计算结果整体转换为 float 型。

(c)ui 转换成 float,计算结果转换成 double。

(d)ival 先转换成 float 型与 fval 相加,结果转换为 double 型,最终结果在转换为 char 型。 

练习4.36:

假设 i 是 int 类型,d 是 double 类型,书写表达式 i *= d 使其执行整数类型的乘法而非浮点类型的乘法。

解答:

 i *= static_cast<int>(d) 

练习4.37:

用命名的强制类型转换改写下列旧式的转换语句。

int i; double d; comst string *ps; char *pc; void *pv;

(a)pv = (void*)ps;

(b)i = int (*pc);

(c)pv = &d;

(d)pc = (char*)pv;

解答:

(a)pv = 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。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值