c++ Primer 第四章:表达式 练习答案记录

c++ Primer 第四章:表达式 练习答案记录

练习题导航

  • c++ Primer 第四章:表达式 练习答案记录
  • 4.1节 基础
    • 4.1.1 基本概念
    • 4.1.2 优先级与结合律
      • 练习4.1 表达式5+10*20/2的求值结果是多少
      • 练习4.2 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致
    • 4.1.3 求值顺序
      • 练习4.3 c++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的利用
  • 4.2节 算术运算符
      • 练习4.4 在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号)的表达式并输出其结果验证之前的推断
      • 练习4.5 写成下列表达式的求值结果
      • 练习4.6 写成一条表达式用于确定一个整数是奇数还是偶数
      • 练习4.7 溢出是何含义?写出三条将导致溢出的表达式
  • 4.3 逻辑和关系运算符
      • 练习4.8 说明在逻辑与、逻辑或及相关性运算符中运算对象求值的顺序
      • 练习4.9 解释在下面的if语句中条件部分的判断过程
      • 练习4.10 为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止
      • 练习4.11 书写一条表达式用于测试四个值a、b、c、d的关系,确保a大于b,b大于c,c大于d
      • 练习4.12 假设i、j、k是三个整数,说明表达式i!=j
  • 4.4 赋值运算符
      • 练习4.13 在下述语句中,当赋值完成后i和d的值分别是多少?
      • 练习4.14 执行下述if语句后将发生什么情况?
      • 练习4.15 下面的赋值是非法的,为什么?应该如何修改?
      • 练习4.16 尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如果修改?
  • 4.5 递增和递减运算符
      • 练习4.17 说明前置运算符和后置运算符的区别
      • 练习4.18 如果第132页那个输出vector对象元素的while循环使用前置递增运算符将得到什么结果?
      • 练习4.19 假设ptr的类型是指向int的指针,vec的类型是vector、ival的类型是int,说明下面表达式是何含义?
  • 4.6 成员访问运算符
      • 练习4.20 假设iter的类型是vector::iterator,说明下面的表达式是否合法。如果合法,表达式的含义是什么?如果不合法错在何处?
  • 4.7 条件运算符
      • 练习4.21 编写一段程序,使用条件运算符从vector中找到哪些元素的值是奇数,然后将这些奇数值翻倍
      • 练习4.22 本节的示例程序将成绩划分成high pass、pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设置为low pass。
      • 练习4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
      • 练习4.24 本节的示例程序将成绩划分成high pass、pass和fail,它的依据是条件运算符满足右结合律。
  • 4.8 位运算符
      • 练习4.25 如果一台机器int占32位、char占8位,用的是Latin-1字符串,其中’q‘的二进制形式是01110001,那么表达式~'q'<<6的值是什么?
      • 练习4.26 在本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况?
      • /练习4.27 下列表达式的结果是什么?
  • 4.9 sizeof运算符
      • 练习4.28 编写一段程序,输出每一种内置类型所占空间的大小
      • 练习4.29 推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你想象的一样吗?如果不一样,为什么?
      • 练习4.30 根据4.12节中的表(第147页),在下述表达式的恰当位置加上括号,使得加上括号之后表达式的含义与原来的含义相同
  • 4.10 逗号运算符
      • 练习4.31 本节的程序使用了前置版本的递增和递减运算符,解释为什么要用前置版本而不用后置版本;要想用后置版本的递增递减运算符需要做哪些改动?使用后置版本重写本节的程序
      • 练习4.32 解释下面这个循环的含义
      • 练习4.33 根据4.12节中的表(第147页)说明下面这条表达式的含义
  • 4.11 类型转换
    • 4.11.1 算术转换
      • 练习4.34 根据本节给出的变量定义,说明在下面的表达式中将发生什么样的类型转换
      • 练习4.35 假设有如下定义
    • 4.11.2 其他隐式类型转换
    • 4.11.3 显式转换
      • 练习4.36 假设i是int类型,d是double类型,书写表达式i*=d使其执行整数类型的乘法而非浮点数类型的乘法
      • 练习4.37 用命名的强制类型转换改写下列旧式的转换语句
      • 练习4.38 说明下面这条表达式的含义
  • 4.12 运算符优先级表

下面练习程序别忘记开头都加上这一语句

#include

4.1节 基础

4.1.1 基本概念

4.1.2 优先级与结合律

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

using namespace std;
int main()
{
	int a = 5 + 10 * 20 / 2;
	cout << a << endl;
}

c++ Primer 第四章:表达式 练习答案记录_第1张图片

练习4.2 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致

using namespace std;
int main()
{
	string vec;
	(*(vec.begin()));        //(a)加括号
	(*(vec.begin()) + 1);    //(b)加括号
}

4.1.3 求值顺序

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

可以,因为这样会使得c++运行时候代码的效率更快,而编译者应该尽量避免产生程序的潜在缺陷

4.2节 算术运算符

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

using namespace std;
int main()
{
	int a = 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2;
	int b = ((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2);
	cout << a << " " << b << endl;
}

c++ Primer 第四章:表达式 练习答案记录_第2张图片

练习4.5 写成下列表达式的求值结果

using namespace std;
int main()
{
	int a = -30 * 3 + 21 / 5;    //-90 + 4 = -86
	int b = -30 + 3 * 21 / 5;    //-30 + 63/5 = -30 + 12 = -18
	int c = 30 / 3 * 21 % 5;     //10*21%5 = 210%5 = 0
	int d = -30 / 3 * 21 % 4;    //-10*21%4 = -210%4 = -2
	cout << a << " " << b << " " << c << " " << d;
}

c++ Primer 第四章:表达式 练习答案记录_第3张图片

练习4.6 写成一条表达式用于确定一个整数是奇数还是偶数

using namespace std;
int main()
{
	int a;
	cout << "请输入一个整数,我将判断是奇数还是偶数" << endl;
	while (cin >> a) {
		if (a % 2 == 0) {
			cout << "偶数" << endl;
		}
		else {
			cout << "奇数" << endl;
		}
	}
}

c++ Primer 第四章:表达式 练习答案记录_第4张图片

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

using namespace std;
int main()
{
	int a = 2147483647;    //2的31次方-1
	a++;
	cout << a << endl;
	short b = 32767;   //2的15次方-1
	b++;
	cout << b << endl;
}

c++ Primer 第四章:表达式 练习答案记录_第5张图片

4.3 逻辑和关系运算符

练习4.8 说明在逻辑与、逻辑或及相关性运算符中运算对象求值的顺序

逻辑与,先判断左边表达式是否为真,否输出false;若为真则判断右边表达式是否为真,真输出true,否输出false
逻辑或,先判断左边表达式是否为真,若为否则判断右边表达式是否为真,真输出true,否输出false;若为真直接输出true
相关性运算符先返回左边比较的结果,再将这个结果与右边表达式比较

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

using namespace std;
int main()
{
	const char* cp = "Hello World";
	if (cp && *cp);   //先判断cp指针所指向的地址是否是空地址,是则输出false,不是则判断指针内的内容是否为0,是则输出false,否则输出true
}

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

using namespace std;
int main()
{
	int i = 0;
	while (cin >> i && i != 42);
}

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

using namespace std;
int main()
{
	int a, b, c, d;
	if (a > b && b > c && c > d);
}

练习4.12 假设i、j、k是三个整数,说明表达式i!=j

首先判断i是否等于j,若等于,则判断k是否大于0,若不等于,则判断k是否大于1

4.4 赋值运算符

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

using namespace std;
int main()
{
	int i;
	double d;
	d = i = 3.5;  //i赋值5后赋值到d上,首先i值为3赋予d后,d=3
	i = d = 3.5;  //d值为3.5,赋值到i上后,i=3
}

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

int main()
{
	if (42 = i);   //非法,42不能被赋值
	if (i = 42);   //i=42,i不等于0,true,则可以执行if语句
}

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

int main()
{
	double dval;
	int ival;
	int* pi;
	dval = ival = pi = 0;  //错误“=”: 无法从“int *”转换为“int”   应该改为*pi
}

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

int main()
{
	if (p = getptr() != 0);  //应该在p后面再加一个=号
	if (i = 1024);  //在i后面加一个=号
}

4.5 递增和递减运算符

练习4.17 说明前置运算符和后置运算符的区别

前置运算符是对变量本身进行加,后置运算符将对象原始值的副本作为右值返回

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

第一次循环则解引用第二个地址;最后一次循环,则解引用vector最后一个地址的后一个位置

练习4.19 假设ptr的类型是指向int的指针,vec的类型是vector、ival的类型是int,说明下面表达式是何含义?

如果表达式不正确,为什么?应该如何修改?

#include
using namespace std;
int main()
{
	int* ptr;
	vector<int> vec;
	int ival;
	ptr != 0 && *ptr++;    //判断ptr是否为0
	ival++&& ival;      //首先判断ival是不是为0,不是的话判断ival+1是不是为0,不是输出true,是则输出false
	vec[ival++] <= vec[ival]; //等价玉vec[ival]<=vec[ival+1]
}

4.6 成员访问运算符

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

#include
#include
using namespace std;
int main()
{
	vector<string>::iterator iter;
	*iter++;  //表示对iter解引用,并将iter指向下一个位置
	(*iter)++; //错误,因为iter是字符串,所有不能进行++
	*iter.empty();//点运算符优先级比解引用高,所以,对一个地址访问成员是错误的
	iter->empty();//相当于(*iter).empty(),对string对象找成员对象是对的
	++* iter;     //字符串不能++
	iter++->empty();//相当于(*iter).empty(),对string对象找成员对象是对的,然后再对iter进行++
}

4.7 条件运算符

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

#include
#include
using namespace std;
int main()
{
	vector<int> a={1,2,3,4};
	vector<int> b;
	for (int i = 0; i < 4; i++) {
		b.push_back((a[i] % 2 != 0) ? (2 * a[i]) : a[i]);
		cout << b[i] << endl;
	}
}

c++ Primer 第四章:表达式 练习答案记录_第6张图片

练习4.22 本节的示例程序将成绩划分成high pass、pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设置为low pass。

要求程序包含两个版本:一个版本只使用条件运算符;另一个版本使用1个或多个if语句。哪个版本的程序更容易理解呢?为什么?

#include
#include
using namespace std;
int main()
{
	//版本一:只使用条件运算符
	int socres;
	cout << "请输入所得分数:" << endl;
	cin >> socres;
	string a = ((socres < 60) ? "fail" : (socres <= 75) ? "loe pass" : (socres <= 90) ? "pass" : "high pass");
	cout << a << endl;

	//版本二:使用1个或多个if语句
	cout << "请输入所得分数:" << endl;
	cin >> socres;
	if (socres < 60) {
		cout << "fail" << endl;
	}
	else if (socres <= 75) {
		cout << "low pass" << endl;
	}
	else if (socres < 90) {
		cout << "pass" << endl;
	}
	else {
		cout << "high pass" << endl;
	}
}

c++ Primer 第四章:表达式 练习答案记录_第7张图片

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

#include
#include
using namespace std;
int main()
{
	string s = "word";
	//string p1 = s + s[s.size() - 1] == 's' ? "" : "s"; //原意是想判断s[s.size()-1]是不是等于s,要是是输出"",要是不是,输出s
	string p1 = s + ((s[s.size() - 1] == 's') ? "" : "s");  //修改后
}

练习4.24 本节的示例程序将成绩划分成high pass、pass和fail,它的依据是条件运算符满足右结合律。

假如条件运算符满足的是左结合律,求值过程将会是怎么样的?

那就比如string a = ((socres < 60) ? “fail” :(socres <= 75) ? “loe pass” : (socres <= 90) ? “pass” : “high pass”);
改成string a = ((socres >90) ? “fail” : (socres >75) ? “loe pass” :(socres >60) ? “pass” : “high pass”);

4.8 位运算符

对于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型

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

首先对q取反则是10001110,再向左移6位,则为10000000,是2的7次方=128

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

超过16位的同学,就算通过了测验也是0,会出现位数不够的情况

/练习4.27 下列表达式的结果是什么?

#include
#include
using namespace std;
int main()
{
	unsigned long u11 = 3, u12 = 7;
	int a = u11 & u12;        //00000011和00000111位与操作,得到00000011,故a=3
	int b = u11 | u12;        //00000011和00000111位或操作,得到00000111,故b=7
	int c = u11 && u12;       //执行与判断,由于两个都不等于0,故c=1
	int d = u11 || u12;       //执行或判断,由于两个都不等于0,故d=1
	cout << a << " " << b << " " << c << " " << d << endl;
}

c++ Primer 第四章:表达式 练习答案记录_第8张图片

4.9 sizeof运算符

sizeof的返回值是一个常量表达式,所以我们可以用sizeof的结果声明数组的维度

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

using namespace std;
int main()
{
	cout << "void: nullptr_t" << sizeof(std::nullptr_t) << " bytes" << endl;
	cout << "bool:" << sizeof(bool) << " bytes" << endl;
	cout << "char:" << sizeof(char) << " bytes" << endl;
	cout << "wchar_t:" << sizeof(wchar_t) << " bytes" << endl;
	cout << "char16_t:" << sizeof(char16_t) << " bytes" << endl;
	cout << "char32_t:" << sizeof(char32_t) << " bytes" << endl;
	cout << "short:" << sizeof(short) << " bytes" << endl;
	cout << "int:" << sizeof(int) << " bytes" << endl;
	cout << "long:" << sizeof(long) << " bytes" << endl;
	cout << "long long:" << sizeof(long long) << " bytes" << endl;
	cout << "float:" << sizeof(float) << " bytes" << endl;
	cout << "double:" << sizeof(double) << " bytes" << endl;
	cout << "long double:" << sizeof(long double) << " bytes" << endl;
	cout << "int8_t:" << sizeof(int8_t) << " bytes" << endl;
	cout << "uint8_t:" << sizeof(uint8_t) << " bytes" << endl;
	cout << "int16_t:" << sizeof(int16_t) << " bytes" << endl;
	cout << "uint16_t:" << sizeof(uint16_t) << " bytes" << endl;
	cout << "int32_t:" << sizeof(int32_t) << " bytes" << endl;
	cout << "uint32_t:" << sizeof(uint32_t) << " bytes" << endl;
	cout << "int64_t:" << sizeof(int64_t) << " bytes" << endl;
	cout << "uint64_t:" << sizeof(uint64_t) << " bytes" << endl;
}

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

#include
using namespace std;
int main()
{
	int x[10];
	int* p = x;
	cout << sizeof(x) / sizeof(*x) << endl;   //sizeof(x)=10,sizeof(*x)=1头指针所在
	cout << sizeof(p) / sizeof(*p) << endl;   //sizeof(p)=2因为地址是int型占2个字节,sizeof(*p)=1头指针所在
}

c++ Primer 第四章:表达式 练习答案记录_第9张图片

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

#include
using namespace std;
int main()
{
	(sizeof x) + y;
	sizeof(p->men[i]);
	(sizeof a) < b;
	sizeof(f());
}

4.10 逗号运算符

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

后置版本需要将之前还没进行修改的值保存下来,以便返回,如果不需要未修改的值就使用前置版本。使用后置版本无需改动。

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

c++ Primer 第四章:表达式 练习答案记录_第10张图片

练习4.32 解释下面这个循环的含义

#include
#include
using namespace std;
int main()
{
	constexpr int size = 5;        //定义一个常量表达式
	int ia[size] = { 1,2,3,4,5 };  //初始化一个含量为5数组             
	for (int* ptr = ia, ix = 0; ix != size && ptr != ia + size; ++ix, ++ptr) { //ptr指向ia的首地址,当ix不等于5且ptr所指向地址没到尾部则循环

	}
}

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

someValue ? ++x,++y : --x,–y; 当someValue不等于0时候,x和y各自加一,等于0时候,x和y各自减一

4.11 类型转换

4.11.1 算术转换

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

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

#include
#include
using namespace std;
int main()
{
	float fval;
	double dval;
	int ival;
	char cval;
	if (fval);    //fval将会转换成bool型
	dval = fval + ival; //fval+ival所得结果是float型,赋值给dval后变成了double型
	dval + ival * cval; //cval变成int型,最终结果变成double型
}

练习4.35 假设有如下定义

请问下面的表达式中发生了隐式类型转换吗?如果有,请指出来

#include
#include
using namespace std;
int main()
{
	char cval;
	int ival = 1;
	unsigned int ui = 2;
	float fval = 1.1;
	double dval;

	cval = 'a' + 3;    //'a'转换成int型和3相加,之后再转换为char型变成'd'
	cout << cval << endl;
	fval = ui - ival * 1.0; //ival变成double型,之后再变成unsigned无符号型,最后变成浮点型
	cout << fval << endl;
	dval = ui * fval;    //fval转换成无符号型int,之后相乘结果转换为double型
	cout << dval << endl;
	cval = ival + fval + dval;  //ival转换成了float型与fval相加,之后相加结果变成double型和dval相加最后变成char型
	cout << cval << endl;
}

c++ Primer 第四章:表达式 练习答案记录_第11张图片

注意:虽然有时候不得不使用强制类型转换,但这种方法本质上是非常危险的

cast-name(expression)
cast-name有static_cast、const_cast(改变表达式的常量属性)、reinterpret_cast(指针对指针使用这个)、dynamic_cast
旧式的强制类型转换
type(expr); 函数形式的强制类型转换
(typr)expr; c语言风格的强制类型转换

4.11.2 其他隐式类型转换

4.11.3 显式转换

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

#include
#include
using namespace std;
int main()
{
	int i = 10;
	double d = 5.5;
	i *= static_cast<int>(d);
}

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

#include
#include
using namespace std;
int main()
{
	int i;
	double d;
	const string* ps;
	char* pc;
	void* pv;
	pv = (void*)ps;
	pv = static_cast<void*>(const_cast<string*>(ps));  //新式

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

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

	pc = (char*)pv;
	pc = reinterpret_cast<char*>(pv);  //新式,指针对指针使用这个
}

练习4.38 说明下面这条表达式的含义

#include
#include
using namespace std;
int main()
{
	double slope = static_cast<double>(j / i);   //将j除以i的结果转换为double型,赋值给slope
}

4.12 运算符优先级表

你可能感兴趣的:(c++学习,c++,开发语言,算法)