【c++ Prime 学习笔记】第4章 表达式
表达式
由一个或多个运算对象组成,对表达式求值返回结果。字面值和变量是最简单的表达式
把
运算符
和运算对象
组合可得到复杂表达式。
4.1 基础
4.1.1 基本概念
一元运算符作用于一个对象,如取
地址符&
、解引用符*
二元运算符作用于两个对象,如
==
、*
三元运算符
?:
。函数调用也是特殊的运算符,它运算对象数量没有限制。
组合运算符和运算对象
理解含有多个运算符的复杂表达式,要先理解运算符的优先级
、结合律
,以及运算对象的求值顺序
。
运算对象转化
常见的是整型提升,如小整型(bool、char、short)被提升为int
重载运算符
运算符作用于类类型的运算对象时,用户可以自行定义其含义。
左值右值
当对象被用作右值时,用的是对象的值(内容)。当对象被用作左值时,用的是对象的身份(在内存中的位置)。
赋值符
=
需要非常量左值作为左侧对象,返回结果也是左值取地址符
&
作用于左值对象,返回指向该对象的指针,该指针是右值内置解引用
、内置下标`[]`、迭代器解引用
、string和vector的下标[]
,它们返回的结果都是左值内置和迭代器的递增
++
递减-
作用于左值对象,其前置版本返回左值
4.1.2 优先级和结合律
复合表达式
是含有两个或多个运算符的表达式优先级
和结合律
决定了运算对象的组合方式。先看优先级,一致时看结合律括号
()
无视优先级和结合律算术运算符和IO运算符都满足
左结合律
4.1.3 求值顺序
求值顺序
定义了多个运算对象哪个先被求值,如f1()*f2()
中哪个函数先被调用对于未指定求值顺序的运算符,若表达式指向并修改了同一个对象,则行为未定义。如
cout<<i<<i++;
明确规定求值顺序的4种运算符:逻辑与
&&
、逻辑或||
、条件?:
、逗号,
最佳实践:
- 拿不准优先级和结合律时,用括号。
- 若表达式某处改变了某对象的值,则其他地方不要使用它。例外:
++iter
4.2 算术运算符
一元运算符优先级最高,其次乘除和求余,最后加减。
上述算术运算符都满足左结合律
算术运算符的运算对象和结果都是右值。
算术表达式求值前,小整型都会被提升。所有对象最终都转换成同一种类型
一元正号、加减都可用于指针。一元正号作用于指针或算术值时,返回(提升后的)副本,一元负号对对象的值取负后,返回(提升后的)副本。
算术运算符的结果可能溢出,其结果与机器相关,不可预知
整数相除还是整数
参与取余%的运算对象必须都是整型,不可用浮点做转换
(-m)/n
和m/(-n)
都等于-(m/n)
,m%(-n)
等于m%n
,(-m)%n
等于-(m%n)
21%6; /*结果是3*/ 21/6; /*结果是3*/
21%7; /*结果是0*/ 21/7; /*结果是3*/
-21%-8; /*结果是-5*/ -21/-8; /*结果是2*/
21%-5; /*结果是1*/ 21/-5; /*结果是-4*/
4.3 逻辑和关系运算符
关系运算符作用于算术或指针类型,逻辑运算符作用于任何能转换为bool的类型。它们的返回类型都是bool型
右值
。逻辑与
&&
、逻辑或||
都是短路求值
,先求左侧,仅由左侧无法确定表达式结果时再求右侧。- 逻辑与
&&
仅当左侧为真
时才求右侧 - 逻辑或
||
仅当左侧为假
时才求右侧
- 逻辑与
逻辑非运算符将运算对象的值取反后返回
关系运算符都满足左结合律,因此不能出现i<j<k这种写法。
进行比较时除非比较对象都是严格的bool类型, 否则不要用true等字面值,因为true提升为整型时是1,不是任何非零值都能true
4.4 赋值运算符
赋值运算符的左侧对象必须是可修改的
左值
,其返回结果就是左侧对象,也是左值
。如果左右类型不匹配,将右侧转为左侧类型。
类类型的赋值运算符由类本身决定,如vector模板
重载
了赋值运算符使其可接收花括号列表作为初值。无论左侧对象的类型是什么,初始值列表都可为空。此时编译器创建一个值初始化的
临时量
来初始化。赋值运算满足
右结合律
,即多重赋值语句a=b=c;
解读为a=(b=c);
,前面所有类型或者和最右侧类型相同,或者可由最右侧类型转换得到。赋值运算符优先级较低
C++允许赋值运算的结果作为条件,所以
=
和==
要分清。复合赋值运算符:
- 算术运算符:
+=
=
=
/=
%=
- 位运算符:
<<=
>>=
&=
|=
^=
- 算术运算符:
复合运算符更快:复合运算符
a+=b
仅求值一次,普通运算符a=a+b
求值两次,一次加法一次赋值。
4.5 递增和递减运算符
++i
被称为前置版本
,i++
被称为后置版本
。前置版本和后置版本的区别
:- 前置版本将对象+1或-1后,将对象
本身
作为左值
返回 - 后置版本将对象+1或-1后,将对象
原始值
的副本
作为右值
返回
- 前置版本将对象+1或-1后,将对象
混用解引用和递增可实现简洁性
cout<<*iter++;
等价于cout<<*(iter++);
或cout<<*iter; ++iter;
由于求值顺序经常未指定,所以不要在一条语句中出现a和a++
如
*beg=toupper(*beg++);
未定义,可被解读为beg=toupper(*beg)
或(beg+1)=toupper(*beg)
4.6 成员访问运算符
ptr->mem
等价于(*ptr).mem
。括号不可省略,因为点优先级比解引用高。箭头运算符作用于指针,得到对象的成员,结果是
左值
。(因为解引用得到的一定是引用,引用是左值)点运算符取决于对象:对象是左值就返回左值,对象是右值就返回右值。
4.7 条件运算符
cond?expr1:expr2
如果expr1和expr2都是左值或能转换为同一种左值类型,则运算结果是左值。否则是右值。
允许在条件运算符的内部嵌套另外一个条件运算符
条件运算符优先级非常低,因此在长表达式之中使用时,需要在两端加上括号
4.8 位运算符
- 移位运算符满足左结合律
unsigned char bits=0233; //10011011
bits<<8; //提升为int大小,再左移8位,00000000 00000000 10011011 00000000
bits<<31; //提升为int大小,再左移31位,10000000 00000000 00000000 00000000
bits>>3; //提升为int大小,再右移3位,00000000 00000000 00000000 00010011
4.9 sizeof 运算符
- sizeof运算符返回一条
表达式
或一个类型
所占的字节数
,满足右结合律
,返回值是size_t
类型的常量表达式
。 - 两种形式:
sizeof(type)
sizeof expr
- sizeof不会计算运算对象的值,所以可以:
可在sizeof里解引用无效指针没有影响,是一种安全的行为。
可在sizeof里用域操作符
::
获取类成员大小,而不需要对象和成员。
- sizeof的结果取决于运算对象的类型:
char型表达式返回1
引用做sizeof返回被
引用对象
所占空间大小指针做sizeof返回
指针本身
所占空间大小解引用指针做sizeof返回
指向对象
所占空间大小数组做sizeof得到
整个数组
所占空间大小,(attention:sizeof不会把数组当指针处理)string或vector求sizeof只返回
固定部分
的大小,不会计算对象实际占用空间
- 常用于计算数组长度的方法是:
sizeof(ia)/sizeof(*ia)
,sizeof返回的是常量表达式,可用于声明新数组。
4.10 逗号运算符
- 含有两个运算对象,遵循从左往右的求值顺序
- 实质上是将多个顺序执行的表达式写为一行的手段。
for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--)
ivec[ix] = cnt;
someValue ? ++x, ++y : --x, --y;
4.11 类型转换
- 隐式转换自动执行,不需程序员介入。算术类型的隐式转换被设计为尽量避免损失精度。
- 隐式转换发生的情形:
- 整型提升
- 条件中非bool转bool
- 初始化时初始值转为变量类型,赋值时右侧对象转为左侧类型
- 算术运算或关系运算中有多种类型,最终会统一
- 函数调用时也会有类型转换
4.11.1 算术转换
算术转换
:把运算对象(算术类型)转为最宽的类型,同时有整型和浮点时将整型转浮点。整型提升
:把小整型转为大整型。- 小整型(bool、char、signed char、unsigned char、short、unsigned short等),只要值能放进int就转为int,放不进int就放进unsigned int
- 宽字符(wchar_t、char16_t、char32_t)提升为int、unsigned int、long、unsigned long、long long、unsigned long long中能装进去的最小者
signed和unsigned的转换
- 若unsigned类型不小于signed类型,直接将signed转为unsigned
- 若unsigned类型小于signed类型,且该unsigned类型的值都能装进该signed类型,则unsigned转为signed
- 若unsigned类型小于signed类型,且该unsigned类型的值不都能装进该signed类型,则signed转为unsigned
bool flag; char cval;
short sval; unsigned short usval;
int ival; unsigned int uival;
long lval; unsigned long ulval;
float fval; double dval;
3.14159L+'a'; //'a'提升为int,再转为long double
dval+ival; //ival转double
dval+fval; //fval转double
ival=dval; //dval切除小数部分转int
flag=dval; //dval是0则false,否则true
cval+fval; //cval提升为int,再转float
sval+cval; //sval和cval都提升为int
cval+lval; //cval转long
ival+ulval; //ival转unsigned long
usval+ival; //未定义,根据unsigned short和int所占空间大小做转换
uival+lval; //未定义,根据unsigned int和long所占空间大小做转换
4.11.2 其他隐式类型转换
数组转指针:
大多数用到数组的表达式中,数组自动转为指向首元素的指针
在表达式中使用函数类型也会转为
函数指针
例外:
decltype
、取地址&
、sizeof
、typeid
运算符不会将数组转指针例外:用引用初始化数组时也不会转指针
指针的转换:
0
或nullpt
r可转为任意指针类型指向任意非常量的指针能转为
void *
指向任意对象的指针能转为
const void *
转成bool类型
转换为常量
- 允许将指向非常量的指针或引用转为指向常量的指针或引用。但反之不可,底层const不可删除。
类类型的转换:类类型可定义转换,但编译器只能执行一种类类型转换。
4.11.3 显示转换
- 强制类型转换
手动指定要转换的变量和要转换为的类型,经常是很危险的。
命名的强制类型转换
cast-name<type>(expression)
expression
是要转换的值type
是转换的目标类型,若type是引用类型,则返回左值。cast-name
是一下一种static_cast
只要不包含底层const,都可使用。例如将大算术类型转为小算术类型、浮点转整型、编译器无法自动执行的类型转换
double d=3.14;
void *p=&d; //任何非常量对象的地址都能放进void *指针
double *dp=stataic_cast<double *>(p); //将指针转回指向double型
dynamic_cast
const_cast
- 只能改变运算的底层const
- 如果对象是常量,用const_cast去掉常量后执行写操作是未定义行为。
- const_cast能改变表达式的常量属性,但不能用const_cast改变表达式类型
const char *pc;
char *p = const_cast<char*>(pc); //正确,但是通过p写值是未定义的行为 const char *cp;
char *q = static_const<char*>(cp); //错误,static_cast 不能转换掉const性质
static_cast<string>(cp); //正确,字符串字面值转成string类型
const_cast<string>(cp) //错误,const只改变常量属性
reinterpret_cast
中的一种。- 为运算对象的位模式提供较低层次上的重新解释,即内存中的bits不变,改变解读方式。它依赖于机器,非常危险。
建议避免使用强制类型转换,尤其是reinterpret_cast
早期C++中,显式类型转换的形式为:
type(expr)
(type)expr
在某处使用旧式强制转换时,若换为const_cast和static_cast也合法,就当作const_cast和static_cast,否则当作reinterpret_cast。因为指代不明,故建议不使用。
4.12 运算符优先级表
【c++ Prime 学习笔记】第4章 表达式的更多相关文章
- The C++ Programming Language 学习笔记 第6章 表达式和语句
1.关于strcpy函数. 书中说c风格的字符串尽量少用,strcpy这样的函数应该也要少用.这里讲这个函数主要是要通过本章课后练习第十题来讲一下前面提及的要点.巩固一下前几章的知识.写了一段,本来感 ...
- Programming Entity Framework-dbContext 学习笔记第五章
### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...
- 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承
<Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...
- Stealth视频教程学习笔记(第二章)
Stealth视频教程学习笔记(第二章) 本文是对Unity官方视频教程Stealth的学习笔记.在此之前,本人整理了Stealth视频的英文字幕,并放到了优酷上.本文将分别对各个视频进行学习总结,提 ...
- Stealth视频教程学习笔记(第一章)
Stealth视频教程学习笔记(第一章) 本文是对Unity官方视频教程Stealth的学习笔记.在此之前,本人整理了Stealth视频的英文字幕,并放到了优酷上.本文将分别对各个视频进行学习总结,提 ...
- 20145330《Java学习笔记》第一章课后练习8知识总结以及IDEA初次尝试
20145330<Java学习笔记>第一章课后练习8知识总结以及IDEA初次尝试 题目: 如果C:\workspace\Hello\src中有Main.java如下: package cc ...
- java JDK8 学习笔记——第16章 整合数据库
第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...
- CSS3秘笈第三版涵盖HTML5学习笔记1~5章
第一部分----CSS基础知识 第1章,CSS需要的HTML HTML越简单,对搜索引擎越友好 div是块级元素,span是行内元素 <section>标签包含一组相关的内容,就像一本书中 ...
- 《DOM Scripting》学习笔记-——第三章 DOM
<Dom Scripting>学习笔记 第三章 DOM 本章内容: 1.节点的概念. 2.四个DOM方法:getElementById, getElementsByTagName, get ...
- The Road to learn React书籍学习笔记(第三章)
The Road to learn React书籍学习笔记(第三章) 代码详情 声明周期方法 通过之前的学习,可以了解到ES6 类组件中的生命周期方法 constructor() 和 render() ...
随机推荐
- You-Get开源在线下载神器,搭配python更加丝滑(文中案例演示)
大家好,我是辰哥 今天给大家介绍一个号称可以下载全网视频.音频.图像的开源库 --you-get you-get 这里说全网可能一点夸张,但如果实际上去使用you-get下载媒体文件(视频.音频.图像 ...
- Mybatis源码解析4——SqlSession
上一篇文章中,我们介绍了 SqlSessionFactory 的创建过程,忘记了的,可以回顾一下,或者看下下面这张图也行. 接下来,可乐讲给大家介绍 Mybatis 中另一个重量级嘉宾--SqlSes ...
- Redis详解(三)——
redis https://www.cnblogs.com/zhangyinhua/p/14504717.html
- openwrt开发笔记二:树莓派刷openwrt
前言及准备 本笔记适用于第一次给树莓派刷openwrt系统的玩家,对刷机过程及注意事项进行了记录,刷机之后对openwrt进行一些简单配置. 使用openwrt源码制作固件需要花费一点时间. 平台环境 ...
- PPPoE技术白皮书(H3C)
PPPoE技术白皮书 关键词:PPP,Ethernet,PPPoE 摘要:PPPoE是一种通过一个远端接入设备为以太网上的主机提供接入服务,并可以对接入的每个主机实现控制和计费的技术.本文介绍了PPP ...
- JAVA安全基础之反射
JAVA安全基础之反射 在JAVA安全中,反射是一个经常使用的技术,所以熟悉使用反射是非常必要的.下面就简单的讲下JAVA的反射的用法 什么是反射 每个类都有对应的Class类对象,该Class类对象 ...
- 【第四篇】- Maven 构建生命周期之Spring Cloud直播商城 b2b2c电子商务技术总结
Maven 构建生命周期 Maven 构建生命周期定义了一个项目构建跟发布的过程. 一个典型的 Maven 构建(build)生命周期是由以下几个阶段的序列组成的: 阶段 处理 描述 验证 ...
- zt:我使用过的Linux命令之ar - 创建静态库.a文件
我使用过的Linux命令之ar - 创建静态库.a文件 本文链接:http://codingstandards.iteye.com/blog/1142358 (转载请注明出处) 用途说明 创建静 ...
- 在 vue-cli 项目中 使用elementUI 的“自定义主题”功能
1.安装elementUI $ npm i element-ui -S 2.安装主题工具 npm i element-theme -g 3.安装chalk主题 npm 安装 npm i element ...
- 在FLASH中读写结构体
在FLASH中读写结构体 注意事项 编程(写数据)地址要对齐 写数据时,我们要指定写入的地址,如果写入地址为非对齐,则会出现编程对齐错误. 比如遵循32位(4字节)地址对齐,你的地址只能是4的倍数.0 ...