c++primer第五版第四章重点笔记

表达式由一个或多个运算对象组成,对表达式求值将得到一个结果;把一个运算符和一个或多个运算对象组合起来可以生成较复杂的表达式。

4.1 基础

(1)一元运算符:作用于一个运算对象,如取地址符&和解引用符*;二元运算符:作用于两个运算对象,如相等运算符==和乘法运算符*。某些运算符既可以是一元运算符也可以是二元运算符,两种用法互不相干。

(2)函数调用也是一种特殊的运算符,对运算对象没有限制。

(3)对具有多个运算符的表达式求值,要根据其优先级结合律和运算对象的求值顺序

(4)表达式求值过程中,运算对象类型有时不同,但只要可以被转换为同一种类型即可。

(5)当运算符作用于类类型对象时,用户可自行定义其含义,称为重载运算符。如第三章中的string对象、vector对象及迭代器所使用的运算符都是重载运算符。重载运算符定义其运算对象的类型和返回值的类型。

(6)C++的表达式中存在的不是左值就是右值;左值表达式的求值结果是一个对象或者一个函数,但某些表达式结果为对象时,它是右值而非左值。
归纳:当一个对象被用作右值时,用的是对象的值;对象被用作左值时,用的是对象的身份。

  • 赋值运算符左侧对象需为左值,得到的结果也为左值;
  • 取地址符&作用于左值运算对象,得到一个指针为右值;
  • 内置解引用运算符*、下标运算符、迭代器解引用运算符、string和vector下标运算符的求值结果均为左值;
  • 内置类型和迭代器的递增递减运算符作用于左值运算对象,其前置版本所得结果也为左值;
  • 算术运算符的运算对象和求值结果都是右值;
  • 逻辑运算符和关系运算符的运算对象和求值结果均为右值

(7)复合表达式的求值首先将运算符和运算对象合理的组合,优先级和结合律决定其组合方式。优先级表见C++primer第五版147页。括号无视优先级和结合律
各运算符满足的结合律:

  • IO相关运算符满足左结合律
  • 算术运算符满足左结合律

(8)虽然上述规则规定了组合方式,但是求值顺序并未说明。而对于那些没有指定执行顺序的运算符,如果表达式指向并修改了同一个对象,将引发错误并产生未定义的行为。 逻辑与&&、逻辑或||、条件运算符?:和逗号运算符,明确规定了求值顺序。

本节建议:某些时候可以用括号强制让表达式的组合关系满足程序逻辑需要;如果改变了某对象的值,则在表达式其他地方不要在使用该对象,除非改变运算对象的子表达式本身就是另一个子表示式的运算对象。

4.2 算术运算符

算术运算符包括优先级最高的一元正号+和一元负号-优先级次高的乘法、除法/和求余%*,优先级最低的加+减-。满足左结合律,运算对象和求值结果都是右值。
(1)一元正号、加法运算符和减法运算符都可作用于指针。

(2)整数相除仍得整数,商的小数部位被弃除。

(3)参与取余运算的运算对象必须为整数类型,如果(m%n)不为零,则取余符号和m相同,即m%(-n)等于m%n(-m)%n等于-(m%n)

(4)算术表达式可能产生未定义的结果:1数学性质本身如除数为0;2是计算机特点如溢出。计算结果超出该类型所能表达的范围时会产生溢出:unsigned int= 0;–int;

4.3逻辑和关系运算符

逻辑运算符作用于任何可转换为布尔值的类型,关系运算符作用于算术类型或指针类型,两者返回值均为布尔类型运算对象和求值结果均为右值
(1)首先逻辑非运算符!优先级最高,满足右结合律。

(2)关系运算符中小于<、大于>、小于等于<=和大于等于>=优先级次高接下来优先级较高的时相等==和不相等!=。关系运算符皆满足左结合律。因为关系运算符求值结果为布尔值,所以不可连写几个关系符。

(3)最后剩下的逻辑运算符中,逻辑与(&&)优先级高于逻辑或(||),满足左结合律。对于逻辑与运算符,当且仅当两个运算对象都为真时结果为真;对于逻辑或运算符,只要两个运算对象中有一个为真结果即为真。
逻辑与和逻辑非都是先求解左侧运算对象再求右侧运算对象,这种策略称为短路求值:当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。

(4)进行比较时除非比较的对象是布尔类型,否则不要使用布尔值字面值true和false作为运算对象。

4.4赋值运算符

赋值运算符的左侧运算对象是一个可修改的左值,满足右结合律,且优先级相对较低。
(1)赋值运算的结果类型即为左侧运算对象的类型,如果左右两个运算类型不同,则右侧运算对象转换为左侧运算对象类型

(2)**如果左侧运算对象是内置类型,那么初始值列表最多只能包含一个值。**对于类类型,赋值运算细节由类本身决定。

(3)右结合律的应用:在多重赋值语句中的每一个对象,它的类型或者与右边对象类型相同,或可由右边对象的类型转换得到。

(4)对对象施以某种运算,然后将计算结果再赋给该对象,由此可得到复合赋值形式算术运算符:+=、-=、*=、/=、%=、;
位运算符:<<=、>>=、&=、^=、|=;

4.5递增和递减运算符

(1)递增和递减运算符都有前置和后置两种版本,这种形式均首先将运算对象加一(减一),不同的是前置版本返回改变后的对象,而后置版本返回的是运算对象改变之前值的副本。

(2)递增递减运算符必须作用于左值运算对象,前置版本返回左值,后置版本返回右值。

(3)因为递增运算符优先级高于解引用符,所以若想在一条复合表达式中即获得变量加减一后的值又想使用它之前的值时,混合使用解引用符和递增递减运算符的后置版本

auto pbeg = v.begin();
while(pbeg != v,end() && *pbeg >= 0)
	cout << *pbeg <<endl;//输出当前值并将pbeg向前移动一个元素
等价于
	cout << *pbeg <<endl;
	++pbeg;

(4)因为运算对象可按任意顺序求值,递增递减会改变运算对象的值,所以要小心使用。如

*beg = toupper(*beg++);

因为赋值运算符左右两端的运算对象都用到了beg,并且右侧的运算对象改变了beg的值,所以该赋值语句时未定义的。

(5)在某些情况下,后置版本会增加一些不必要的工作,所以非必须情况否则不用递增递减的后置版本

4.6 成员访问运算符

点运算符和箭头运算符均可访问成员,点运算符获取类对象的成员;箭头运算符与点运算符相关:ptr->mem等价于(*ptr).men。
(1)箭头运算符作用于一个指针类型的运算对象,结果是一个左值点运算符结果取决于成员所属的对象的为左值还是右值

(2)点运算符优先级高于解引用,因此执行解引用运算符的子表达式两端需加括号。

//*p.size() 错误,p是指针没有size成员应改为
(*p),size();

4.7条件运算符

条件运算符(?:)为以下形式:cond ? expr1 : expr2;将if-else逻辑嵌入到单个表达式中。表示cond条件为真时,计算expr1并返回结果,否则计算expr2并返回结果
(1)当条件运算符两个表达式均为左值,或者两者能转换为同一种左值类型,则结果为左值,否则为右值。

(2)允许条件运算符嵌套使用,但因为随着嵌套层数的增加,代码刻度想下降,因此嵌套最好不要超过两到三层

(3)条件运算符优先级非常低,所以在长表达式中嵌套条件子表达式时通常需要在两端加上括号。

	//练习4.22
	unsigned grade; //无符号变量存放分数
	string result; //
	while (cin >> grade){ //依次读取分数直至到文件结尾
		result = (grade > 90) ? "high pass" : (grade > 75) ? "pass" : (grade > 60 && grade < 75) ? "low pass" : "fail";
		cout << grade << "属于"; 
		cout << result << endl;
	}

4.8位运算符

位运算符作用于整数类型的运算对像,并将运算对象看作二进制位的集合,位运算符提供检查和设置二进制位的功能。由在位运算符中优先级最高的位求反运算符(~)、优先级次之的左移<<右移>>运算符、优先级继续下降一位的位与&运算符、优先级继续下降的位异或^运算符和优先级最低的位或|运算符。位运算符皆遵循左结合律
(1)移位运算符是对运算对象执行基于二进制位的移动操作,令左侧运算对象的内容按照右侧运算对象的要求引动指定位数,移动后的左侧对象的拷贝作为结果。右侧对象不可为负,且小于结果位数,移出边界的舍弃。

(2)左移运算符在右侧插入值为0的二进制位;右移运算符取决于左侧对象类型,若左侧为无符号类型,在左侧插入0;若左侧为有符号类型,则在左侧插入符号位的副本或者0;

(3)位求反运算符将运算对象逐位求反后生成一个新值。char类型求反先将其提升为int类型,提升过程中原有位保持不变,高位加0即可。

(4)位与运算符&,如果两个运算对象对应位置均为1则结果才为1,否则为0位或运算|两个运算对象对应位至少有一个为1结果为1,否则为0位异或运算^,如果两个运算对象对应位有且只有一个1结果为1,否则为0。均为在两个运算对象上逐位执行相应逻辑操作。

(5)移位运算符其重载版本为IO操作,重载运算符的优先级和结合律与其内置版本相同。

4.9sizeof运算符

sizeof返回表达式或类型名字所占的字节数,类型为size_t的常量表达式,有两种形式:sizeof (type)和sizeof expr第二种形式返回表达式结果类型的大小
(1)sizeof并不实际计算其运算对象的值,所以指针类型为无效指针也不会有什么影响。

(2)sizeof运算结构取决其作用的类型

  • char或类型为char的表达式执行sizeof,结果为1;
  • 引用类型得到被引用对象所占空间大小;
  • 指针 得到指针本身所占空间大小;
  • 解引用指针 得到指针所指对象所占空间大小,指针不需要有效;
  • 数组, 得到整个数组所占空间的大小,等价于对数组个元素求sizeof后求和的结果;
  • string对象和vector对象, 返回该类型固定部分的大小。

(3)sizeof不会把数组转换为指针

(4)sizeof后得int:4、char:1、long:4、float:4、double:8。

4.10逗号运算符

(1)逗号运算符含有两个运算对象,由左向右依次求值
(2)逗号运算符首先对左侧表达式求值,然后将求值结果丢弃,其真正结果是右侧表达式的求值结果,若右侧运算对象为左值,则求值结果也为左值。

4.11类型转换

(1)如果两种类型可以相互转换,则它们是关联的;关联的类型,在编程过程中可以相互替代。

(2)一个表达式中存在不同类型的运算对象时,编译器会将其根据类型转换规则转换一致后进行运算,无需程序员介入称为隐式转换
出现隐式类型转换的情况:

  • 大多数表达式中,比int小的整型值首先提升为较大的整数类型
  • 条件中,非布尔值转为布尔值
  • 初始化过程中,初始值转为变量类型;赋值语句中,右侧对象转为左侧对象类型;
  • 算术或关系运算中包含多种类型运算对象,转为同一种类型;
  • 函数调用
  • 表达式中有整数和浮点型时,整型转为浮点型

(3)算术转换是将一种算术类型转为另一种算数类型,其中运算符的运算对象将转为最宽的类型。

(4)整型提升将小整数类型转换为较大的整数类型
(bool,signed char, unsigned char, short, unsigned short)转为(int或unsigned int)
(wchar_t, char16_t, char32_t)提升为(int, unsigned int, long, unsigned long, long long, unsigned long long )中最小的一种

(5)若某个对算对象为无符号类型,则转换结果依赖机器中各整数类型的相对大小。
转换顺序

  1. 首先整型提升,若结果类型匹配则无需继续,否则第二步
  2. 判断运算对象如果符号一致,均为无符号或均为带符号,则小类型转为大类型,否则第三步
  3. 两个运算对象符号不一致,一个带符号,一个无符号,且无符号类型不小于带符号类型,则带符号对象转为无符号,否则第四步
  4. 带符号类型大于无符号类型,若无符号类型所有制能存在带符号类型中,无符号转为有符号,否则有符号转为无符号。
    具体研究143页示例

(6)前面已知,大多数用到数组的表达式,数组会转化为指针,则下面几种情况不会发生数组转指针:当数组用做decltype关键字的参数、作为取地址符、sizeof和typeid的运算对象时,还有用一个引用初始化数组时

(7)指针转换:现有的常量整数值0和字面值nullptr可转为任意指针类型、指向非常量的指针可转为void*、指向任意对象的指针可转为const void。有继承关系的类也有指针转换方式。

(8)允许将指向非常量类型的指针转换为指向相应常量类型的指针。但相反的转换不存在

(9)编译器自动执行类类型定义的转换,但一次只能执行一种类类型转换。

(10)强制类型转换,即使用某种方法将运算对象显式的转换为另一种类型。一种命名转换形式为cast-name(expression);
type是转换的目标,expression是要转换的值。若type是引用类型则结果为左值
cast-name是下面中的其中一种:

  1. static_cast任何具有明确定义的类型转换,只要不包含底层const,都可使用。在将较大的算数类型赋给较小类型时,显式的告诉编译器我们知道但不在乎精度损失。对于编译器无法自动执行的类型转换也非常有用。
void *p = &d;
double *dp = static_cast<double*>(p);//确保指针的值不变,必须确保转换后的类型就是指针所指类型
  1. const_cast:只能改变运算对象的底层const。将常量对象转为非常量对象的行为称为去掉const性质。const_cast常用于函数重载的上下文中。
  2. reinterpret_cast:为运算对宪法的位模式提供较低层次上的重新解释。如
int *ip;
char *pc = reinterpret_cast<char*>(ip);

必须牢记pc所指的真实对象为int而不是字符,如果把pc当普通字符使用会产生错误
4. dynamic_cast

(11)reinterpret_cast本质上依赖于机器,若想安全的使用reinterpret_cast,必须对涉及的类型和编译器实现转换的过程都非常了解。

(12)避免强制类型转换,强制类型转换总是充满风险。

(13)旧版的强制转换包含两种形式

 type (expr); //函数类型的强制类型转换
 (type) expr; //c语言风格的强制类型转换
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C Primer》是一本针对C语言初学者的经典教材,第五版的第六章主要介绍了函数的基本概念和用法。 在第六章中,教材首先介绍了函数的定义和调用。函数是具有独立功能的代码块,可以通过函数名来调用。函数由函数头、参数、函数体和返回值组成。函数头包括函数类型、函数名和参数类型,参数用于接收传递给函数的值,返回值用于将结果返回给调用者。 接着,教材详细介绍了函数的参数传递方式,包括按值传递、按引用传递和按指针传递。按值传递是指将参数的值复制给形参,按引用传递是指将参数的引用传递给形参,而按指针传递是将参数的地址传递给形参。不同的传递方式在函数内部对参数进行操作时,会对应不同的结果。 此外,教材还讲解了函数的返回值和函数的调用顺序。函数的返回值类型由函数头中的类型确定,可以是任意类型。当函数需要返回多个值时,可以使用结构体或指针进行返回。函数的调用顺序决定了函数执行的顺序,即哪个函数先执行,哪个函数后执行。 在函数的实现过程中,教材介绍了函数的定义和声明、局部变量和全局变量、递归等内容。通过这些知识点的学习,读者可以了解到函数的具体细节和一些实践技巧。 总的来说,第五版的第六章通过清晰的语言和丰富的例子,循序渐进地讲解了函数的基本概念和用法。学完这一章,读者将能够理解函数的定义、调用、参数传递、返回值和实现等方面的知识,为之后的学习和实践打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值