0x03_表达式
表达式
基础
优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。在大多数情况下,不会明确指定求值的顺序。
int i = f1() * f2();
我们知道f1和f2一定会在执行乘法之前被调用,但是我们无法知道到底f1在f2之前调用还是f2在f1之前调用。对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。
有4种运算符明确规定了运算对象的求值顺序:&&
、||
、? :
、,
。
算术运算符
在除法运算中,如果两个运算对象的符号相同则商为正(不为0),否则商为负。C++语言早期本本允许结果为负值的商象上或向下取整,C++11新标准则规定商一律向0取整(即直接切除小数部分)。
除了-m导致溢出的特殊情况,其他时候:
(-m) / n == m / (-n) == -(m / n) m % (-n) == m % n (-m) % n == -(m % n)
sizeof运算符
sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,其所得的值是一个size_t类型的常量表达式。运算符的运算对象有两种形式:
sizeof (type) sizeof expr
第二种形式中,sizeof返回的是表达式结果类型的大小。与众不同的一点是,sizeof并不实际计算其运算对象的值。
Sales_data data, *p; sizeof(Sales_data); sizeof *p; // p所指类型的空间大小,等价于sizeof(Sales_data)
因为sizeof不会实际求运算对象的值,所以即使p是一个无效的指针也不会有影响。
C++11新标准允许使用作用域运算符来获取类成员的大小。通常情况下只有通过类的对象才能访问到类的成员,但是sizeof运算符无须我们提供一个具体的对象。
sizeof运算符的结果部分地依赖于其作用的类型:
- 对char或类型为char的表达式执行sizeof运算,结果得1。
- 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
- 对指针执行sizeof运算得到指针本身所占空间的大小。
- 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效。
- 对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并求和。sizeof运算不会把数组转换成指针来处理。
- 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。
因为sizeof的返回值是一个常量表达式,所以我们可以用sizeof的结果声明数组的维度。
类型转换
何时发生隐士类型转换:
- 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
- 在条件中,非布尔值转换成布尔类型。
- 初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。
- 如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
- 函数调用时也会发生类型转换。
算术转换
整型提升
负责把小整数类型转换成较大的整数类型。对于bool、char、signed char、unsigned char、short和unsigned short等类型来说,只要它们所有可能的值都能存在int里,它们就会提升成int类型;否则,提升成unsigned int类型。
较大的char类型(wchar_t、char16_t、char32_t)提升成int、unsigned int、long、unsigned long、long long和unsigned long long中最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。
无符号类型的运算对象
首先执行整型提升,如果结果的类型匹配,无需进行进一步的转换。如果两个提升后的运算对象的类型要么都是带符号的、要么都是无符号的,则小类型的运算对象转换成较大的类型。
如果一个运算对象是无符号类型、另外一个运算对象是带符号类型,而且其中的无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的,需要注意的是,如果有符号的恰好为负值,则会有副作用。
剩下一种情况是带符号类型大于无符号类型,此时转换的结果依赖于机器。如果无符号类型的所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型,如果不能,那么带符号类型的运算对象转换成无符号类型。
其他隐式类型转换
数组转换成指针
指针的转换
常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void *;指向任意对象的指针能转换成const void *。
转换成布尔类型
转换成常量
如果T是一种类型,能将指向T的指针或引用分别转换成指向const T的指针或引用。
类类型定义的转换
显式转换
命名的强制类型转换:
一个命名的强制类型转换具有如下形式:
cast-name<type>(expression);
其中type是转换的目标类型而expression是要转换的值。如果type是引用类型,则结果是左值。cast-name是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一种。cast-name指定了执行的是哪种转换。
static_cast
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
const_cast
const_cast只能改变运算对象的底层cosnt。
const char *pc; char *p = const_cast<char *>(pc);
如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用cosnt_cast执行写操作就会产生未定义的后果。
只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型。const_cast常常用于有函数重载的上下文中。
reinterpret_cast
reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。
int *ip; char *pc = reinterpret_cast<char *>(ip);
pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。
运算符优先级表
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)