1.前置递增运算符和解引用运算符的优先级相同并且都比算术运算符优先级高。
2.在表达式*++iter中,递增运算符改变iter的值。iter的值又是解引用运算符的运算对象。
3.运算符%俗称取余或取模。负责计算两个整数相除所得的整数。参与取余运算的运算对象必须是整数类型。取余运算的定义,如果m和n是整数且n非0,则表达式(m/n)*n+m%n的求值结果与m相等。
新规定中:
// (-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
对于逻辑与运算(&&)来说,当且仅当两个运算对象都为真时结果为真。
对于逻辑或运算(||)来说,只要两个运算对象中的一个为真结果就为真。
逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种策略成为短路求值。
对于逻辑与运算符来说,当且仅当左侧运算对象为真时才对右侧运算对象求值。
对于逻辑或运算符来说,当且仅当左侧运算对象为假时才对右侧运算对象求值。
//解释:
//首先检查index是否到达string的末尾,
//以此确保只有当index在合理的范围内才会计算右侧运算对象的值
index! = s.size() && !isspace(s[index])
//s是对常量的引用,元素既没有被拷贝也没有被改变
for(const auto &s:text){
cout << s;
//遇到空字符或者遇到句号换行
if(s.empty() || s[s.size() - 1 ] == '.')
cout<< endl;
else
cout << " ";
//解释
//if语句的条件部分首先检查s是否是一个空string,
//如果是。则不论右侧对象的值如何都要换行。
//只有当string对象非空时,才需要求第二个运算对象的值。
//在短路求值策略中只有当s为非空的情况下才会用下标运算去访问。
⬇️后置版本,将原始值存储下来以便返回这个未修改的内容
int i = 0,j;
j = i++;
cout <<"i = "<< i << " j = "<<j<<endl;
//i = 1 j = 0
⬇️前置版本,把值加1后直接返回改变了运算对象
int i = 0,j;
j = ++i;
cout <<"i = "<< i << " j = "<<j<<endl;
//i = 1 j = 1
⬇️这种写法很混乱,请避免
int i = 0;
i = i++;//警告 对 "i "的多次无序修改。
cout << i <<endl;
// i = 0
⬇️ 完整版本例子:
int i = 0,j;
j = i++; //后置版本得到递增之前的值 i = 1 j = 0
j = ++i;//前置版本得到递增之后的值
cout <<"i = "<< i << " j = "<<j<<endl;
//i =2 j = 2
//-----------------------
int i = 0,j;
j = ++i;//前置版本得到递增之后的值 j = 1 i= 1
j = i++; //后置版本得到递增之前的值
cout <<"i = "<< i << " j = "<<j<<endl;
//i =2 j = 1
左移运算符 << 在右侧插入值为0的二进制位。
右移运算符 >> 的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位,如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位。
位求反运算符 ~将运算对象逐位求反后生成一个新值,将1置为0,将0置为1.
位与,位或,位异或运算符
对于位与&运算符来说,如果两个运算对象的对应位置都是1则运算结果中该位为1,否则为0.
对于位与|运算符来说,如果两个运算对象的对应位置至少有一个为1则运算结果中该位为1,否则为0.
对于位异或^运算符来说,如果两个运算对象的对应位置有且只有一个为1则运算结果中该位为1,否则为0.
我们举一个使用位运算符的例子:
假设班级中有30个学生,老师每周都会对学生进行一次小测验,测验的结果只有通过和不通过两种。为了更好地追踪测验的结果,我们用一个二进制位代表某个学生在一次测验中是否通过,显然全班的测验结果可以用一个无符号整数来表示:
unsigned long quizl = 0;
我们把这个值当成是位的集合来使用
定义quiz1的类型是unsigned long,这样,quizl在任何机器上都将至少拥有32位,给quiz1赋一个明确的初始值,使得它的每位在开始时都有统一且固定的值。
教师必须有权设置并检查每一个二进制位。例如,我们需要对序号为27的学生对应的位进行设置,以表示他通过了测验。
为了达到这一目的,首先创建一个值,该值只有第27位是1其他位都是0,然后将这个值与quizl进行位或运算,这样就能强行将quiz1的第27位设置为1,其他位都保持不变。
为了实现本例的目的,我们将quizl的低阶位赋值为0、下位赋值为I,以此类推,最后统计quizl各个位的情况。
使用左移运算符和一个unsigned long类型的整数字面值1 (参见2.1.3 节,第35页)就能得到一个表示学生27通过了测验的数值:
//生成一个值,该值只有第27位为1
//1UL的低阶位上有一个1.除此之外(至少)还有31个值为0的位。
//之所以使用unsigned long类型,是因为int类型只能确保占用16位,而我们至少需要27位。
//⬇️这条表达式通过在值为1的那个二进制位后面添加0,使得它向左移动了27位。
1UL << 27
//接下来将所得的值与quiz1进行位或运算。为了同时更新quiz1的值,使用了一条复合赋值语句
quizl |= 1UL << 27;//表示学生27通过了测验
//⬆️上面的表达式等价于 quizl = quizl| 1UL << 27;
//------------------------------------------------------------------------
//假定教师在重新核对测验结果时发现学生27实际上并没有通过测验,
//他必须要把第27位的值置为0.此时我们需要使用一个特殊的整数,它的第27位是0、其他所有位都是1.
//将这个值与quizl进行位与运算就能实现目的了:⬇️
//通过将之前的值按位求反得到一个新值,除了第27位外都是1,只有第27位的值是0.
//随后将该值与quiz1进行位与运算,所得结果除了第27位外都保持不变。
quizl &= ~(1UL << 27); //学生27没有通过测验
//------------------------------------------------------------------------
//我们试图检查学生27测验的情况到底怎么样:⬇️
//我们将quizl和一个只有第27位是1的值按位求与,如果quiz1的第27位是1,
//计算的结果就是非0 (真);否则结果是0.
bool status - quiz1 & (1UL << 27); //学生27是否通过了测验?
☑️常量整数值0或者字面值nullstr能转换成任意指针类型。
☑️指向任意非常量的指针能转换成void*。
☑️指向任意对象的指针能转换成const void*。
☑️指针或算术类型的值为0,可以转换结果是false,否则是true。
☑️允许将指向非常量类型的指针转换成指向相应的常量类型的指针,对于引用也是这样的。
int i;
const int &j = i;//非常量转换成const int的引用
const int *p = &i;//非常量的地址转换成const地址
int &r = j,*q = p;//错误:不允许const转换成非常量
static_cast
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast.
通过将一个运算对象强制成double类型就能使表达式执行浮点数除法⬇️:
double slope = static_cast<double>(j) / i;
void * p = &d;//任何非常量对象的地址都能存入void*
double *dp = static_cast<double*>(p);//将void*转换回初始的指针类型
const_cast
const_cast只能改变运算对象的底层const
const char *pc;
char * p = const_cast<char*>(pc);//正确,但是通过p写值是未定义行为
对于将常量对象转换成非常量对象的行为,我们称其为“去掉const性质”。一但我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法行为。如果对象是一个常量,在使用const_cast执行写操作就会产生为定义的后果。