C++ Primer(第5版) 课后答案 第四章

4.1:表达式5+10*20/2的求值结果是多少?

根据运算符的优先级和结合律,此表达式可以看作 5+( (10 * 20) / 2),结果为105

4.2:根据运算符优先级表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。

(a) *vec.begin()                        //*(vec.begin())

(b) *vec.begin()+1                    //(*(vec.begin))+1

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

可以接受,我们可以自己判断,如果改变了某个运算对象的值,在表达式的其他地方就不再使用这个运算对象,但改变运算对象的子表达式本身就是另外一个子表达式的运算对象时该规则无效。

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

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

添加括号后:( (12 / 3) * 4)+(5 * 15)+( (24%4) / 2) = 91

#include<iostream>
using namespace std;
int main()
{
	int num = 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2;
	cout << num;
}
//输出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:写出一条表达式用于确定一个整数是奇数还是偶数。

#include<iostream>
using namespace std;
int main()
{
	int num;
	cin >> num;
	if (num % 2)
		cout << "奇数";
	else
		cout << "偶数";
}

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

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

#include<iostream>
using namespace std;
int main()
{
	short a = 32768;
	cout << a << endl;		//输出-32768
	int b = 2147483648;
	cout << b << endl;		//输出-2147483648
	int c = -b - 1;
	cout << c;				//输出2147483647
}

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

对于逻辑与&&:当且仅当左侧运算对象为真时才对右侧运算对象求值

对于逻辑或 | |:当且仅当左侧运算对象为假时才对右侧运算对象求值

对于相等性运算符:如果两侧运算对象类型不同,进行类型转换

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

const char* cp = "Hello World";
if(cp && *cp)

先判断cp,是一个指针,指向了字符数组的第一个元素,不为空,结果为真

接着判断*cp,解引用操作,得到字符'H',为真。

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

#include<iostream>
using namespace std;
int main()
{
	int n;
	while (cin >> n && n != 42)
	{ }
}

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

#include<iostream>
using namespace std;
int main()
{
	int a = 4, b = 3, c = 2, d = 1;
	if (a > b && b > c && c > d)
		cout << "a>b、b>c、c>d" << endl;
}

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

根据优先级,先比较 j 和 k 的大小,关系运算符的运算结果是布尔值,如果 j < k,运算结果为true;如果 j >=k ,运算结果为false。接着进行相等性测试,首先布尔值将先转换整型,true转换为1;false转换为0,与i比较是否相等,如果相等,运算结果为true,如果不相等,运算结果为false。

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

#include<iostream>
using namespace std;
int main()
{
	int i;
	double d;
	d = i = 3.5;		//3 3

	i = d = 3.5;		//3 3.5
	cout << i << " " << d;
}

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

if (42 = i)        //会报错,字面值42是右值

if (i = 42)         //i=42,赋值语句结果为左侧运算对象,赋值后为42,非0,因此为真

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

int main()
{
	double dval;
	int ival;
	int* pi;
	dval = ival = pi = 0;
	//pi类型时int*型,ival是int型,不能把指针类型的值赋给int
}

改正:

int main()
{
	double dval;
	int ival;
	int* pi;
	pi = 0;
	dval = ival = 0;
}

4.16:尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?

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

此表达式中 != 运算符优先级高于 = 运算符,因此会先比较 getPtr( )的返回值与0是否相等,比较的结果是一个布尔值,相等为true,不相等为false,再将结果赋给p,结果为1或0。

这样的过程和结果可能与设想的过程并不一致,因此可以用括号将上述语句修改为

if( (p = getPtr()) != 0)

(b) if(i = 1024)

此表达式为赋值表达式,值为1024,转换为布尔类型永远为true,而期待的可能是比较 i 与1024是否相等,因此我们可以把上述表达式改为

if(i ==1024)

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

前置递增运算符:j=++i,首先将 i 加1,然后将改变后的对象作为求值结果

后置递增运算符:j=i++,也会将韵算对象加1,但是求值结果是运算对象改变之前那个值的副本

两种运算符必须作用于左值运算对象。前置版本将对象本身作为左值返回,后置版本将对象原始值的副本作为右值返回

建议:如果不需要修改前的值,都采用前置版本。

4.18:如果以下输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?

	auto pbeg = v.begin();
	while (pbeg != v.end() && *pbeg >= 0)
		cout << *pbeg++ << endl;

如果改为前置递增运算符,第一个元素的值将无法输出,而且如果序列中没有负值,后面可能试图解引用一个不存在的元素。

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

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

如果指针不是空指针,就将指针指向后一位,接着返回指针改变之前指向的对象,若未非0,表达式为真

(b) ival++ && ival

首先ival自增1,然后返回ival原始值的副本,如果不为0,就对右边ival求值,此时的ival应是加1之后的,如果不为0,表达式为真

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

表达式的求值顺序未知,若是先求左侧,右侧的ival就应该是加了1以后的,若是先求右侧,ival就应该是原始值,修改如下:

++ival;

vec[ival] <= vec[ival+1] ;

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

(a) *iter++;

合法,自增运算符优先级大于解引用,iter++使iter加1,接着返回iter原始值的副本作为iter++表达式的结果,接着*解引用的是未增加之前的iter副本

(b) (*iter)++;

不合法,先解引用iter,得到了一个sring元素,string不能自增,不能通过编译

(c) *iter.empty()

不合法,迭代器没有empty()成员函数

(d) iter->empty();

合法,iter->empty()等价于(*iter).empty(),如果iter指向的string为空就返回true,否则返回false

(e) ++*iter;

不合法,原因同b,不能通过编译

(f) iter++->empty();

合法,先判断迭代器指向的值是否为空,再对迭代器加1,指向下一个元素

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

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> sp{ 1,2,3,4,5,6 };
	cout << "Original data is:";
		for (auto it : sp)
			cout << it << " ";
		cout << endl;
		cout << "After changing is:";
		for (auto it : sp)
			cout << ((it % 2) ? 2 * it : it)<<" ";
}

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

只用条件运算符:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int grade = 0;
	cin >> grade;
	string finalgrade = (grade > 90) ? "high pass"
				      : (grade > 75) ? "pass"
					  : (grade > 60) ? "low pass" : "fail";		
	cout << finalgrade;
}

使用if语句

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int grade = 0;
	cin >> grade;	
	string finalgrade;
	if (grade > 90)
		cout << "high pass";
	else if (grade > 75)
		cout << "pass";
	else if (grade > 60)
		cout << "low pass";
	else
		cout << "fail";
	cout << finalgrade;
}

if语句更容易理解,可读性比条件运算符多次嵌套好。

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

string s = "word";

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

根据运算符优先级,将首先通过s.size()求出s中字符的个数为4,接着s[4-1]访问s中下标为3的字符'd',将字符字面值与string对象s相加,相加后的string对象s为"wordd",接着与字符's'比较是否相等,但是string对象无法与字符字面值s进行==比较,因此错误。

修改如下:

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

上述过程到访问s中下标为3的字符时,若字符为's',则不向string对象s加入任何值,若字符不为's',则向string对象s结尾加上's‘。

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

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

左边的条件运算构成了右边的条件运算的判断条件的表达式,如果输入的值大于90,表达式的结果就应该是”high pass",字符串转换成布尔类型是有限制的,显然此字符串不可以转换,会出错。

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

根据运算符优先级,先对'q'按位取反,

在内存中字符q为:00000000 00000000 00000000 01110001

按位取反后为      :   11111111 11111111 11111111 10001110

按位左移6位后为: 11111111 11111111 11100011 10000000

是一个负数,在内存中以补码存取,如果输出,输出的是化为原码的十进制数

原码 :10000000 00000000 00011100 10000000

化为十进制后的数为 -7296

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

不确定30个学生的成绩都能准确表示,因为unsigned int只能确保占用16位,而此例至少需要30位,因此选择unsigned long。

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

#include<iostream>
using namespace std;
int main()
{
	//3: 00000000 00000000 00000000 00000011
	//7: 00000000 00000000 00000000 00000111

	unsigned long ul1 = 3, ul2 = 7;

	cout << (ul1 & ul2);		//3
	cout << (ul1 | ul2);		//7
	cout << (ul1 && ul2);		//1
	cout << (ul1 || ul2);		//1
}

后两个表达式的运算符是逻辑运算符。

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

#include<iostream>
using namespace std;
int main()
{
	cout << "bool的大小为:" << sizeof(bool) << endl;			//1
	cout << "char的大小为:" << sizeof(char) << endl;			//1
	cout << "short的大小为:" << sizeof(short) << endl;			//2
	cout << "int的大小为:" << sizeof(int) << endl;				//4
	cout << "long的大小为:" << sizeof(long) << endl;			//4
	cout << "long long的大小为:" << sizeof(long long) << endl;	//8
	cout << "float的大小为:" << sizeof(float) << endl;			//4
	cout << "double的大小为:" << sizeof(double) << endl;		//8
}

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

#include<iostream>
using namespace std;
int main()
{
	int x[10];
	int* p = x;
	cout << sizeof(x) / sizeof(*x) << endl;			//10
	cout << sizeof(p) / sizeof(*p) << endl;			//2
}

第一个表达式计算的是数组x中的元素数量

第二个表达式的sizeof(p)计算的是一个指针的大小,由于我的电脑是64位的,因此大小为8,sizeof(*p)计算的是指针所指向的对象所占空间大小,为4。

4.30:根据运算符优先级表,在下述表达式的适当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同。

(a) sizeof x+y                //(sizeof x)+y

(b) sizeof p-> men[i]     //sizeof (p->men[i])

(c)sizeof a<b                //(sizeof a)<b

(d)sizeof f()                   //sizeof (f())

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

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> ivec{1,2,3,4,5};
	vector<int>::size_type cnt = ivec.size();
	for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix, --cnt)
		ivec[ix] = cnt;
	for (auto it : ivec)
		cout << it << " ";
}
//输出5 4 3 2 1

个人认为如果使用了后置版本就会把原始的值存储下来,而前置版本则是直接返回改变了以后的运算对象,但是我们并不需要原始值了,因此使用后置版本是一种浪费,其他原因暂时想不到,而用后置改写也改动不大。

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> ivec{ 1,2,3,4,5 };
	vector<int>::size_type cnt = ivec.size();
	for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--)
		ivec[ix] = cnt;
	for (auto it : ivec)
		cout << it << " ";
}

如上,改变之后的版本输出依然正确。

4.32:解释下面这个循环的含义。

int main()
{
	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和指针都自增1。

4.33:根据运算符优先级表说明下面这条表达式的含义。

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

由于逗号表达式优先级最低,此表达式可以看成

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

如果someValue为真,x自增1,y自增1,后又自减1,y的值不变

如果someValue为假,x自减1,y自减1

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

  float fval;        int ival;        double dval;        char cval;

(a) if (fval)                        //fval如果是0,则转换为false,否则转换为true

(b) dval = fval + ival;        //ival首先被转换为float,将fval+ival的结果转换为double

(c) dval + ival * cval;        //cval先整型提升成int,与ival相乘后的结果被转换成double

4.35:假设有如下的定义,请回答在下面的表达式中发生了隐式类型转换吗?如果有,指出来。

char cval;        int ival;        unsigned int ui;        float fval;        double dval;

(a) cval = 'a' + 3;                     

//'a'整型提升为int,与3相加后的结果转换为char赋给cval

(b) fval = ui - ival * 1.0;             

//ival转换为double,与1.0相乘,ui转换为double,与ival*1.0相减,相减后结果转换为float赋给fval

(c) dval = ui * fval;

//ui转换为float,与fval相乘,乘积转换为double赋给dval

(d) cval = ival +fval + dval;

//ival转换为float,与fval相加后的结果转换为double,与dval相加后的结果转换为char,赋给cval

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

i *= static_cast<int>(d);

#include<iostream>
using namespace std;
int main()
{
	int i = 2;
	double d = 2.5;
	i *= d;						//5
	i *= static_cast<int>(d);	//4
	cout << i ;
}

原表达式输出结果为5,更改后为4,因为原表达式i类型转换为了double型,执行浮点型的乘法,而更改后用强制类型转换将d转换为int型,执行整型的乘法。

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

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

(a) pv = (void*)ps;                        // pv = const_cast<void*>(ps);

(b) i = int(*pc);                              // i = static_cast<int>(*pc)

(c) pv = &d;                                  // pv = static_cast<void*>(&d)

(d) pc = (char*) pv;                       // pc = static<char*>(pv)

4.38:说明下面这条表达式的含义。

double slope = static_cast<double> ( j / i );

将 ( j / i )的值强制转换为double类型,赋给slope

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值