首页 > 编程语言 >c++_primer之第四章

c++_primer之第四章

时间:2024-11-15 11:57:14浏览次数:1  
标签:运算 int 练习 c++ 运算符 对象 求值 primer 第四章

4.1 节练习

练习 4.1

在算术运算符中,乘法和除法的优先级相同,且均高于加减法的优先级。因此上式的计算结果应该是 105,在编程环境中很容易验证这一点。

练习 4.2

在本题涉及的运算符中,优先级最高的是成员选择运算符和函数调用运算符,其次是解引用运算符,最后是加法运算符。因此添加括号后的等价的式子是:

(a) *(vec.begin())

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

下述程序可以用来验证我们的推断:

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>

using namespace std;

int main()
{
	vector<int> vec;
	srand((unsigned)time(NULL));
	cout << "系统自动为向量生成一组元素......" << endl;
	for (int i = 0; i != 10; i++)
		vec.push_back(rand() % 100);
	cout << "生成的向量数据是:" << endl;
	for (auto c : vec)
		cout << c << " ";
	cout << endl;
	cout << "验证添加的括号是否正确:" << endl;
	cout << "*vec.begin()的值是:" << *vec.begin() << endl;
	cout << "*(vec.begin())的值是:" << *(vec.begin()) << endl;
	cout << "*vec.begin()+1 的值是:" << *vec.begin() + 1 << endl;
	cout << "(*(vec.begin()))+1 的值是:" << (*(vec.begin())) + 1 << endl;
	return 0;
}

输出如下:

img

练习 4.3

正如题目所说,C++只规定了非常少的二元运算符(逻辑与运算符、逻辑或运算符、条件(?:)运算符、逗号运算符)的求值顺序,其他绝大多数二元运算符的求值顺序并没有明确规定。这样做提高了代码生成的效率,但是可能引发潜在的缺陷。

关键是缺陷的风险有多大?我们知道,对于没有指定执行顺序的运算符来说,
如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为;而如果运算对象彼此无关,它们既不会改变同一对象的状态也不执行IO任务,则函数的调用顺序不受限制。

就作者的观点而言,这样的做法在一定程度上是可以接受的,前提是在编写程
序时注意以下两点:一是拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求;二是一旦改变了某个运算对象的值,在表达式的其地方就不要再使用这个运算对象了。

附上书上原话:

img

img

4.2 节练习

练习 4.4

不赘叙了

练习 4.5

本题用到两条典型的算术运算规则。

一是除法:整数相除结果还是整数,如果商含有小数部分,直接弃除。尤其当除法的两个运算对象的符号不同时,商为负,C++11 新标准规定商一律向 0 取整。

二是取余:如果取余的两个运算对象的符号不同,则负号所在的位置不同运算结果也不相同,m%(-n)等于 m%n,(-m)%n 等于-(m%n)。

因此,本题的求值结果是:

(a) -30 * 3 + 21 / 5 = -86

(b) -30 + 3 * 21 / 5 = -18

(c) 30 / 3 * 21 % 5 = 0

(d) -30 / 3 * 21 % 4 = -2

附上书上原话:

img
img

练习 4.6

下面的表达式可以用于确定一个整数是奇数还是偶数,假设该整数名为 num,则表达式 num % 2 == 0 为真时 num 是偶数,该表达式为假时 num 是奇数。

练习 4.7

溢出是一种常见的算术运算错误。因为在计算机中存储某种类型的内存空间有限,所以该类型的表示能力(范围)也是有限的,当计算的结果值超出这个范围时,就会产生未定义的数值,这种错误称为溢出。

假定编译器规定 int 占 32 位,则下面的 3 条表达式都将产生溢出错误:

int i = 2147483647 + 1; 
int j = -100000 * 300000; 
int k = 2024 * 2024 * 2024 * 2024; 

img

显然与我们的预期不相符,是溢出之后产生的错误结果。

4.3 节练习

练习 4.8

对于逻辑与运算符来说,当且仅当两个运算对象都为真时结果为真;对于逻辑或运算符来说,只要两个运算对象中的一个为真结果就为真。

逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。

这种策略就是短路求值。其策略是:对于逻辑与运算符来说,当且仅当左侧运算对象为真时才计算右侧运算对象;对于逻辑或运算符来说,当且仅当左侧运算对象为假时才计算右侧运算对象。

值得注意的是,逻辑与运算符和逻辑或运算符是 C++中仅有的几个规定了求值顺序的运算符。相等性运算符的两个运算对象都需要求值,C++没有规定其求值顺序。

练习 4.9

cp 是指向字符串的指针,因此上式的条件部分含义是首先检查指针 cp 是否有效。如果 cp 为空指针或无效指针,则条件不满足。如果 cp 有效,即 cp 指向了内存中的某个有效地址,继续解引用指针 cp 并检查 cp 所指的对象是否为空字符'\0',如果 cp 所指的对象不是空字符则条件满足;否则不满足。

在本例中,显然初始状态下 cp 指向了字符串的首字符,是有效的;同时当前cp 所指的对象是字符'H',不是空字符,所以 if 的条件部分为真。

练习 4.10

最简洁的形式是:

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

该语句首先检查从输入流读取数据是否正常,然后判断当前读入的数字是否是42,遇到 42 则条件不满足,退出循环。

还有一种形式也可以实现同样的目的:

int num; 
while( cin >> num ) 
{ 
 if(num == 42) 
 break; 
 // 其他操作
}

练习 4.11

要想用一条表达式测试 a、b、c、d 的关系,并确保 a 大于 b、b 大于 c、c 大于 d,应该写成:

a > b && b > c && c > d

切勿写成:

a > b > c > d

因为关系运算符满足左结合律且运算的结果是布尔值,所以把几个关系运算符连写在一起必然会产生意想不到的结果。

a > b > c > d 的实际求值过程是先判断 a > b 是否成立,成立则为1,不成立则为 0;接着用这个布尔值(1 或 0)与 c比较,所得的结果仍然是一个布尔值;最后再用刚刚得到的布尔值与 d 进行比较。

显然这一过程与用户的书写原意背道而驰。

练习 4.12

C++规定<、<=、>、>=的优先级高于==和!=,因此上式的求值过程等同于i!=(j<k),意即先比较 j 和 k 的大小,得到的结果是一个布尔值(1 或 0);然后判断 i 的值与之是否相等。

附上书上原话:

img

4.4 节练习

练习 4.13

由题意可知,(a)式的含义是先把 3.5 赋值给整数 i,此时发生了自动类型转换,小数部分被舍弃,i 的值为 3;接着 i 的值再赋给双精度浮点数 d,所以 d 的值也是3。

(b)式的含义是先把 3.5 赋值给双精度浮点数 d,因此 d 的值是 3.5;接着 d 的值再赋给整数 i,此时发生了自动类型转换,小数部分被舍弃,i 的值为 3。

本题涉及的知识点有两个:

第一,如果赋值运算符左右两个运算对象的类型不同,则右侧运算对象转换成左侧运算对象的类型;

第二,赋值运算符满足右结合律。

附上书上原话:

img

img

练习 4.14

第一条语句发生编译错误,因为赋值运算符的左侧运算对象必须是左值,字面值常量 42 显然不是左值,不能作为左侧运算对象。

第二条语句从语法上来说是正确的,但是与程序的原意不符。程序的原意是判断 i 的值是否是 42,应该写成 i==42;而 i=42 的意思是把 42 赋值给 i,然后判断i 的值是否为真。因为所有非 0 整数转换成布尔值时都对应 true,所以该条件是恒为真的。

本题涉及的知识点有两个:

第一,赋值运算符的左侧运算对象必须是左值,右侧运算对象可以是左值,也可以是右值;

第二,赋值运算符与相等性运算符在作为 if 语句的条件时含义不同。

练习 4.15

该赋值语句是非法的,虽然连续赋值的形式本身并没有错,但是参与赋值的几个变量类型不同。其中,dval 是双精度浮点数,ival 是整数,pi 是整型指针。

自右向左分析赋值操作的含义,pi=0 表示 pi 是一个空指针,接下来ival=pi试图把整型指针的值赋给整数,这是不符合语法规范的操作,无法编译通过。稍作调整,就可以把上述程序改为合法。

double dval; int ival; int *pi; 
dval = ival = 0; 
pi = 0;

练习 4.16

(a)的原意是把 getPtr()得到的指针赋值给 p,然后判断 p 是否是一个空指针,但上述表达式的实际执行结果与之相距甚远。因为赋值运算符的优先级低于不相等运算符,所以真正的表达式求值过程是先判断 getPtr()的返回值是否为空指针,如果是则 p=0,否则 p=1,最后以 p 的值作为 if 语句的条件。

要想符合原意,应该修改为:

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

(b)的原意是判断 i 的值是否是 1024,但上述表达式实际上是把 1024 赋值给 i,然后以 i 作为 if 语句的条件。因为所有非 0 整数转换成布尔值时都对应 true,所以该条件是恒为真的。

附上书上原话:

img

4.5 节练习

练习 4.17

递增和递减运算符有两种形式:前置版本和后置版本。

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

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

我们的建议是,除非必须,否则不用递增(递减)运算符的后置版本。前置版本的递增运算符避免了不必要的工作,它把值加 1 后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改之前的值,那么后置版本的操作就是一种浪费。

对于整数和指针类型来说,编译器可能对这种额外的工作进行了一定的优化;但是对于相对复杂的迭代器类型来说,这种额外的工作就消耗巨大了。建议养成使用前置版本的习惯,这样不仅不需要担心性能问题,而且更重要的是写出的代码会更符合编程人员的初衷。

练习 4.18

代码如下:

img

前置递增运算符先将运算对象加 1,然后把改变后的对象作为求值结果;后置递增运算符也将运算对象加 1,但是求值结果是运算对象改变之前那个值的副本。

简言之,如果在一条表达式中出现了递增运算符,则其计算规律是:++在前,先加 1,后参与运算;++在后,先参与运算,后加 1。

基于上述分析,本题不应该把 while 循环的后置递增运算符改为前置递增运算符。如果这样做了,会产生两个错误结果:一是无法输出 vector 对象的第一个元素;二是当所有元素都不为负时,移动到最后一个元素的地方,程序试图继续向前移动迭代器并解引用一个根本不存在的元素。

练习 4.19

(a)的含义是先判定指针 ptr 是否为空,如果不为空,继续判断指针 ptr 所指的整数是否为非 0 数。如果非 0,则该表达式的最终求值结果为真;否则为假。最后把指针 ptr 向后移动一位。该表达式从语法上分析是合法的,但是最后的指针移位操作不一定有意义。如果 ptr 所指的是整型数组中的某个元素,则 ptr 可以按照预期移动到下一个元素。如果 ptr 所指的只是一个独立的整数变量,则移动指针操作将产生未定义的结果。

(b)的含义是先检查 ival 的值是否非 0,如果非 0 继续检查(ival+1)的值是否非 0。只有当两个值都是非 0 值时,表达式的求值结果为真;否则为假。在 4.1.3 节中我们学习到,如果二元运算符的两个运算对象涉及同一个对象并改变对象的值,则这是一种不好的程序写法,应该改写。所以按照程序的原意,本式应该改写成 ival && (ival + 1)

(c)的含义是比较 vec[ival]vec[ival + 1] 的大小,如果前者较小则求值结果为真,否则为假。与(b)式一样,本式也出现了二元运算符的两个运算对象涉及同一个对象并改变对象值的情况,应该改写为 vec[ival] <= vec[ival + 1]

4.6 节练习

练习 4.20

(a)是合法的,后置递增运算符的优先级高于解引用运算符,其含义是解引用当前迭代器所处位置的对象内容,然后把迭代器的位置向后移动一位。

(b)是非法的,解引用iter得到vector对象当前的元素,结果是一个string,显然 string 没有后置递增操作。

(c)是非法的,解引用运算符的优先级低于点运算符,所以该式先计算 iter.empty(),而迭代器并没有定义 empty 函数,所以无法通过编译。

(d)是合法的,iter->empty(); 等价于 (*iter).empty();。解引用迭代器得到迭代器当前所指的元素,结果是一个 string,显然字符串可以判断是否为空,empty 函数在此处有效。

(e)是非法的,该式先解引用 iter,得到迭代器当前所指的元素,结果是一个string,显然 string 没有后置递增操作。

(f)是合法的,iter++->empty();等价于(*iter++).empty();。含义是解引用迭代器当前位置的对象内容,得到一个字符串,判断该字符串是否为空,然后把迭代器向后移动一位。

对于(e),附上书上原话:

img

4.7 节练习

练习 4.21

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>

using namespace std;

int main()
{
	vector<int> vInt;
	const int sz = 10;				// 使用 sz 作为数组的维度
	srand((unsigned)time(NULL));	// 生成随机数种子
	// 使用普通 for 循环为数组赋初值
	cout << "数组的初始值是:" << endl;
	for (int i = 0; i != sz; ++i)
	{
		vInt.push_back(rand() % 100); // 生成 100 以内的随机数
		cout << vInt[i] << " "; // 使用下标运算符输出数组内容
	}
	cout << endl;
	// 使用范围 for 循环把数组中的奇数翻倍
	for (auto& val : vInt)
		val = (val % 2 != 0) ? val * 2 : val; // 条件表达式
		// 使用普通 for 循环和迭代器输出数组的当前值
	cout << "调整后的数组值是:" << endl;
	for (auto it = vInt.cbegin(); it != vInt.cend(); ++it)
		cout << *it << " ";
	cout << endl;
	return 0;
}

输出如下:

img

练习 4.22

使用条件运算符:

#include <iostream> 
#include <string> 
using namespace std;
int main()
{
	string finalgrade;
	int grade;
	cout << "请输入您要检查的成绩:" << endl;
	// 确保输入的成绩合法
	while (cin >> grade && grade >= 0 && grade <= 100)
	{
		// 使用三层嵌套的条件表达式
		finalgrade = (grade > 90) ? "high pass"
			: (grade > 75) ? "pass"
			: (grade > 60) ? "low pass" : "fail";
		cout << "该成绩所处的档次是:" << finalgrade << endl;
		cout << "请输入您要检查的成绩:" << endl;
	}
	return 0;
}

输出如下:

img

使用 if 语句:

#include <iostream> 
#include <string> 
using namespace std;
int main()
{
	string finalgrade;
	int grade;
	cout << "请输入您要检查的成绩:" << endl;
	// 确保输入的成绩合法
	while (cin >> grade && grade >= 0 && grade <= 100)
	{
		// 使用 if 语句实现
		if (grade > 90)
			finalgrade = "high pass";
		else if (grade > 75)
			finalgrade = "pass";
		else if (grade > 60)
			finalgrade = "low pass";
		else
			finalgrade = "fail";
		cout << "该成绩所处的档次是:" << finalgrade << endl;
		cout << "请输入您要检查的成绩:" << endl;
	}
	return 0;
}

输出如下:

img

练习 4.23

题目中的几个运算符的优先级次序从高到低是加法运算符、相等运算符、条件运算符和赋值运算符,因此式子的求值过程是先把 s 和 s[s.size() - 1]相加得到一个新字符串,然后该字符串与字符's'比较是否相等,这是一个非法操作,并且与程序的原意不符。

要想实现程序的原意,即先判断字符串 s 的最后一个字符是否是's',如果是,什么也不做;如果不是,在 s 的末尾添加一个字符's',我们应该添加括号强制限定运算符的执行顺序。

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

练习 4.24

原文的程序是:

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

根据左结合律的含义,该式等价于:

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

先考查 grade > 90 是否成立,如果成立,第一个条件表达式的值为"high pass";如果不成立,第一个条件表达式的值为 grade < 60。这条语句是无法编译通过的,因为条件运算符要求两个结果表达式的类型相同或者可以互相转化。即使假设语法上通过,也就是说,第一个条件表达式的求值结果分为 3 种,分别是"high pass"、1 和 0。接下来根据第一个条件表达式的值求解第二个条件表达式,求值结果是"fail"或"pass"。上述求值过程显然与我们的期望是不符的。

4.8 节练习

练习 4.25

在位运算符中,运算符~的优先级高于<<,因此先对 q 按位求反,因为位运算符的运算对象应该是整数类型,所以字符'q'首先转换为整数类型。如题所示,char 占 8 位而 int 占 32 位,所以字符'q'转换后得到 00000000 0000000 00000000 01110001,按位求反得到 11111111 11111111 11111111 10001110;接着执行移位操作,得到11111111 11111111 11100011 10000000。

C++规定整数按照其补码形式存储,对上式求补,得到 10000000 0000000 00011100 10000000,即最终结果的二进制形式,转换成十进制形式是-7296。

练习 4.26

根据题目的要求,班级中有 30 个学生,我们用一个二进制位代表某个学生在一次测验中是否通过,则原书中使用 unsigned long 作为 quiz1 的数据类型是恰当的,因为 C++规定 unsigned long 在内存中至少占 32 位,这样就足够存放 30 个学生的信息。

如果使用 unsigned int 作为 quiz1 的类型,则由于 C++规定unsigned int所占空间的最小值是 16,所以在很多机器环境中,该数据类型不足以存放全部学生的信息,从而造成了信息丢失,无法完成题目要求的任务。

练习 4.27

ul1 转换为二进制形式是:00000000 00000000 00000000 00000011,ul2 转换为二进制形式是:00000000 00000000 00000000 00000111。各个式子的求值结果分别是:

(a)按位与,结果是:00000000 00000000 00000000 00000011,即 3。

(b)按位或,结果是:00000000 00000000 00000000 00000111,即 7。

(c)逻辑与,所有非 0 整数对应的布尔值都是 true,所以该式等价于 true && true,结果为 true。

(d)逻辑或,所有非 0 整数对应的布尔值都是 true,所以该式等价于 true || true,结果为 true。

4.9 节练习

练习 4.28

#include <iostream> 
using namespace std;
int main()
{
	cout << "类型名称\t" << "所占空间" << endl;
	cout << "bool\t\t" << sizeof(bool) << endl;
	cout << "char\t\t" << sizeof(char) << endl;
	cout << "wchar_t\t\t" << sizeof(wchar_t) << endl;
	cout << "char16_t\t" << sizeof(char16_t) << endl;
	cout << "char32_t\t" << sizeof(char32_t) << endl;
	cout << "short\t\t" << sizeof(short) << endl;
	cout << "int\t\t" << sizeof(int) << endl;
	cout << "long\t\t" << sizeof(long) << endl;
	cout << "long long\t" << sizeof(long long) << endl;
	cout << "float\t\t" << sizeof(float) << endl;
	cout << "double\t\t" << sizeof(double) << endl;
	cout << "long double\t" << sizeof(long double) << endl;
	return 0;
}

输出如下:

img

练习 4.29

sizeof(x)的运算对象 x 是数组的名字,求值结果是整个数组所占空间的大小,等价于对数组中所有的元素各执行一次 sizeof 运算并对所得结果求和。尤其需要注意,sizeof 运算符不会把数组转换成指针来处理。在本例中,x 是一个 int数组且包含 10 个元素,所以 sizeof(x)的求值结果是 10 个 int 值所占的内存空间总和。

sizeof(*x) 的运算对象 *x 是一条解引用表达式,此处的 x 既是数组的名称,也表示指向数组首元素的指针,解引用该指针得到指针所指的内容,在本例中是一个 int。所以 sizeof(*x)在这里等价于 sizeof(int),即 int 所占的内存空间。

sizeof(x)/sizeof(*x)可以理解为数组 x 所占的全部空间除以其中一个元素所占的空间,得到的结果应该是数组 x 的元素总数。实际上,因为 C++的内置数组并没有定义成员函数 size(),所以通常无法直接得到数组的容量。本题所示的方法是计算得到数组容量的一种常规方法。

sizeof(p)的运算对象 p 是一个指针,求值结果是指针所占的空间大小。

sizeof(*p) 的运算对象 *p 是指针 p 所指的对象,即 int 变量 x,所以求值结果是 int 值所占的空间大小。

在Visual Studio 2019 中运行结果如下:

img

在该编译环境中int 占 4 字节,指针也占 4 字节

在 Dev-C++ 中运行结果如下:

img

在该编译环境中 int 占 4 字节,指针占 8 字节。

练习 4.30

(a)的含义是先求变量 x 所占空间的大小,然后与变量 y 的值相加;因为 sizeof 运算符的优先级高于加法运算符的优先级,所以如果想求表达式 x+y 所占的内存空间,应该改为 sizeof(x + y)。

(b)的含义是先定位到指针 p 所指的对象,然后求该对象中名为 mem 的数组成员第 i 个元素的尺寸。因为成员选择运算符的优先级高于 sizeof 的优先级,所以本例无须添加括号。

(c)的含义是先求变量 a 在内存中所占空间的大小,再把求得的值与变量 b 的值比较。因为 sizeof 运算符的优先级高于关系运算符的优先级,所以如果想求表达式 a<b 所占的内存空间,应该改为 sizeof(a < b)。

(d)的含义是求函数 f()返回值所占内存空间的大小,因为函数调用运算符的优先级高于 sizeof 的优先级,所以本例无须添加括号。

4.10 节练习

练习 4.31

本题从程序运行结果来说,使用前置版本或后置版本是一样的,这是因为递增递减运算符与真正使用这两个变量的语句位于不同的表达式中,所以不会有什么影响。

使用后置版本重写的程序是:

vector<int>::size_type cnt = ivec.size(); 
// 将从 size 到 1 的值赋给 ivec 的元素。
for(vector<int>::size_type ix = 0;ix != ivec.size(); ix++, cnt--) 
ivec[ix] = cnt; 

根据 4.5 节的介绍我们知道,除非必须,否则不用递增(递减)运算符的后置版本。前置版本的递增运算符避免了不必要的工作,它把值加 1 后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改之前的值,那么后置版本的操作就是一种浪费。

就本题而言,使用前置版本是更好的选择。

练习 4.32

首先定义一个常量表达式 size,它的值是 5;接着以 size 作为维度创建一个整型数组 ia,5 个元素分别是 1~5。

for 语句头包括三部分:第一部分定义整型指针指向数组 ia 的首元素,并且定义了一个整数 ix,赋给它初值 0;第二部分判断循环终止的条件,当 ix 没有达到size 同时指针 ptr 没有指向数组最后一个元素的下一位置时,执行循环体;第三部分令变量 ix 和指针 ptr 分别执行递增操作。

练习 4.33

C++规定条件运算符的优先级高于逗号运算符,所以 someValue ? ++x, ++y : --x, --y 实际上等价于 (someValue ? ++x, ++y : --x), --y。它的求值过程是,首先判断 someValue 是否为真,如果为真,依次执行++x 和 ++y,最后执行--y;如果为假,执行--x 和--y。

检验一下:

#include <iostream> 
using namespace std;
int main()
{
	int x = 10, y = 20;
	// 检验条件为真的情况
	bool someValue = true;
	someValue ? ++x, ++y : --x, --y;
	cout << x << endl;
	cout << y << endl;
	cout << someValue << endl;
	x = 10, y = 20;
	// 检验条件为假的情况
	someValue = false;
	someValue ? ++x, ++y : --x, --y;
	cout << x << endl;
	cout << y << endl;
	cout << someValue << endl;
	return 0;
}

输出如下:

img

当 someValue 取值为 true 时,依次执行++x、++y、--y,也就是说,x 的值加 1 变为 11,y 的值先加 1 后减 1 保持不变,还是 20。

当 someValue 取值为 false 时,依次执行--x、--y,x 和 y 的值各减少 1 变为 9 和 19。

4.11 节练习

练习 4.34

(a)if 语句的条件应该是布尔值,因此 float 型变量 fval 自动转换成布尔值,转换规则是所有非 0 值转换为 true,0 转换为 false。

(b)ival转换成float,与fval求和后所得的结果进一步转换为double类型。

(c)cval 执行整型提升转换为 int,与 ival 相乘后所得的结果转换为 double类型,最后再与 dval 相加。

练习 4.35

(a)字符'a'提升为 int,与 3 相加所得的结果再转换为 char 并赋给cval。

(b)ival 转换为 double,与 1.0 相乘的结果也是 double 类型,ui 转换为double 类型后与乘法得到的结果相减,最终的结果转换为 float 并赋给 fval。

(c)ui 转换为 float,与 fval 相乘的结果转换为 double 类型并赋给 dval。

(d)ival 转换为 float,与 fval 相加所得的结果转换为 double 类型,再与dval 相加后结果转换为 char 类型。

练习 4.36

使用 static_cast 把 double 类型的变量 d 强制转换成 int 类型,就可以令表达式 i*=d 执行整数类型的乘法。语句的形式应该是:

i *= static_cast<int>(d);

验证一下:

#include <iostream>
#include <typeinfo>

using namespace std;

int main()
{
	int i = 2;
	double d = 3.5;
	cout << (i *= static_cast<int>(d)) << endl;
	cout << typeid(i *= static_cast<int>(d)).name() << endl;
	return 0;
}

输出如下:

img

练习 4.37

(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

把 j/i 的值强制类型转换成 double,然后赋值给 slope。请注意,如果 i 和 j 的类型都是 int,则 j/i 的求值结果仍然是 int,即使除不尽也只保留商的整数部分,最后再转换成 double 类型。

标签:运算,int,练习,c++,运算符,对象,求值,primer,第四章
From: https://www.cnblogs.com/hisun9/p/18547697

相关文章

  • C++之OpenCV入门到提高005:005 图像操作
    一、介绍今天是这个系列《C++之Opencv入门到提高》得第五篇文章。这篇文章也不难,介绍如何图像的基本操作,比如:读取一张图片的像素值,如何修改一张图片中的像素值,如何读取一张图片,如何保存一张图片等等,这都是基础,为以后的学习做好铺垫。虽然操作很简单,但是背后有很多东西需......
  • C++ RAII 范式指南
    1.RAII概述RAII(ResourceAcquisitionIsInitialization)是C++中最重要的资源管理机制之一,它将资源的生命周期与对象的生命周期绑定,确保资源的安全使用和自动释放。历史背景:RAII概念由BjarneStroustrup在1984-1989年间提出最早用于解决C++异常处理中的资源泄......
  • Windows下搭建Cmake编译环境进行C/C++文件的编译
    文章目录1.下载Cmake2.安装MinGW-w643.进行C/C++文件的编译1.下载Cmake网址:https://cmake.org/download/  下载完成后安装,勾选“AddCMaketothesystemPATHforthecurrentuser"  点击Finish完成安装,在cmd窗口验证一下是否安装成功,出现如下图情况则安装成......
  • Qt/C++地图高级绘图/指定唯一标识添加删除修改/动态显示和隐藏/支持天地图高德地图百
    一、前言说明已经有了最基础的接口用来添加覆盖物,而且还有通过进入覆盖物模式动态添加覆盖物的功能,为什么还要来个高级绘图?因为又有新的需求,给钱就搞,一点底线都没有。无论哪个地图厂家,提供的接口都是没有唯一标识参数的,也就类似于学号,这就是需要自己主动定一个属性用来存储唯一标......
  • 构造函数C++
    1.构造函数的介绍功能:专门用于对象的初始化工作,在类的对象创建时定义初始状态特点构造函数的名字和类名是相同的构造函数是没有返回值类型的,也不能写void。可以有形参(也可以重载)在创建对象的时候,会自动调用。而且是一定会调用,但是只会调用一次,不能通过已有......
  • 每日OJ题_牛客_计算字符串的编辑距离_DP_C++_Java
    目录牛客_计算字符串的编辑距离_DP题目解析C++代码Java代码牛客_计算字符串的编辑距离_DP计算字符串的编辑距离_牛客题霸_牛客网描述:Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换......
  • C++函数的返回值在内存中的传递过程及什么是虚拟内存,为什么要使用虚拟内存,虚拟内存可
    1) C++函数的返回值在内存中的传递过程在C++中,函数返回值在内存中的传递过程如下:基本数据类型返回值传递当函数返回基本数据类型(如`int`、`double`、`char`等)时,函数会将返回值存储在一个临时的寄存器中。然后,调用函数的地方会从这个寄存器中获取返回值,并将其存储到相应......
  • 什么是C++命名空间 有什么作用?如何定义使用命名空间?且交代命名空间是否允许嵌套?
    1)什么是C++命名空间有什么作用?命名空间的定义:在C++中,命名空间(Namespace)是一种将代码组织成逻辑组的机制,用于避免不同代码模块之间的命名冲突。它提供了一个声明区域,在该区域内可以定义各种类型、函数、变量等,并且这些定义的名称在该命名空间内是唯一的。命名空间的作用:......
  • C++基础编程(一)
    If语句,条件运算符&&||,运算符优先级,for循环语句,switch语句,continue,break,do,while打印一个锥形1~9矩阵,打印9*9乘法表,For(初始化;条件;每次循环必执行语句)输出abcd....ABCD....0123....输出从1~1000,能被7整除的数While输入一组数,0为结束,计算他们的和......
  • C++ 友元跨命名空间使用
    以+运算符重载为例:#include<iostream>#include<string>//前置声明是必须的namespacemydog{classDog;}namespacemyadd{mydog::Dogoperator+(constmydog::Dogdog1,constmydog::Dogdog2);}namespacemydog{classDog{friend......