编译软件为 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。