目录
运算符优先级
结合律:左结合律就是从左往右计算,右结合律就是从右往左计算。
1. 算术运算符
算术运算符:加、减、乘、除、一元正号、一元负号、求余
注意:
1. % 与 / 运算符在处理负号上有不同,(-a)/b 与 a/(-b) 都等于 -(a/b)。 但是 (-a)%(b)= -(a%b)
而且a%(-b)= a%b、(-a)%(-b)= -(a%b)。
2.参与%运算的必须是整型类型。
3.算术异常:溢出(所求的值大于类型所能表示的范围)、数学性质(如除数时0等等)。
-21%-8 = -5;
21%-5 = 1;
-21/-8 = 2;
21/-5 = -4;
2. 逻辑和关系运算符
逻辑运算符:!、&&、||
关系运算符:<、>、<=、>=、==、!=
注意:
1.运算结果都是布尔型。
2.布尔型只有在值为0时才为false,其余情况都为true,无论正负。
3. 赋值运算符
赋值运算符:=
注意:
1.赋值运算符的优先级是所有运算符里比较低的,所以在使用时请据情况加上()。
2.其满足右结合律,即可以这样赋值:a=b=0; b的类型必须可以转换为a的类型或者和a的类型一样。
4. 递增和递减运算符
递增和递减运算符:前置递增递减(++i)、后置递增递减(i++)
前置递增递减:将改变后的对象作为求值结果,作为左值返回
后置递增递减:将改变前的对象的副本作为求值结果,将对象原始值的副本作为右值返回
用法:
auto pebg=v.begin();
for(pebg!=v.end()&&*pbeg>=0)
cout<< *pebg++ << endl; //因为后置递增运算符的优先级高于*,所以会先输出pebg的值,然后pebg+1。
建议:建议除非必须,则尽量少使用后置递增递减,因为其会产生一个副本造成浪费,而对于复杂的迭代器而言,这会造成很大的运行开销。并且,尽量不要把递增递减运算符作用的对象和对象本身共同用在同一个语句里,因为这样容易引起未定义行为,而造成报错。
vec[ival++] <= vec[ival] // err 会产生未定义行为,如vec[ival]<=vec[ival] 或 vec[ival]<=vec[ival+1]
ival++ && ival // true ival && ival+1
vector<string>::iterator
(*iter)++; // err 因为,string没有递增递减运算
++*iter; // err 原因和上面的一样,*和前置++的运算符是同级的,前置++和*都是右结合律,所以会先执行*iter,此时是string类型,而string没有递增递减运算符
5. 成员访问运算符
成员访问运算符:. 和 ->
(*p).size();
p->size();
这两个效果是一样的。注意:(*p).size() 因为 * 的运算符要低于 . 运算符,所以要加上括号。
6. 条件运算符
条件运算符:cond ? expr1 : expr2;
流程:cond是ture,则执行expr1,是false,则执行expr2
int a[]{ 10,74,80,99 };
for (auto grade : a)
{
cout << (grade > 90 ? "优秀" : (grade > 75 ? "良好" : (grade > 60 ? "及格" : "不及格")))<<endl;
}
注意:
1.条件运算符可以嵌套使用,但为了程序的可读性,最好不要超过三层嵌套
2.条件运算符的优先级很低,为避免错误在条件运算符里嵌套表达式时要注意添加括号
7. 位运算符
位运算符作用于整数类型的运算对象,是直接对二进制位进行操作的,它在运算时,如果运算对象是小整形则会进行整型提升(见[C++] 类型转换),提升为大整型,提升为大整型之后,高位默认为0。
建议:使用位运算符来处理无符号类型。
7.1 移位运算符
移位运算符:<<(左移运算符)、>>(右移运算符)
逻辑:两种运算符实际上就是对二进制位的移位操作,比如:左移运算符,val << x ,就是将val的二进制对象里的内容向左移动x位,然后将移动后的对象拷贝作为求值结果。
规则:
左移运算符:1.移动的位数,即右侧运算对象不能为负数,并且值必须小于结果的位数。 2.移出边界之外的值将会被舍弃。
右移运算符:1.如果左侧运算对象是无符号类型,在左侧插入值为0的二进制位,如果是有符号位,则在左侧插入符号位的副本。
unsigned char bits=0233; // 1001 1011
bits<<8;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 1001 1011
//2.向左移动8位
// 0000 0000 0000 0000 1001 1011 0000 0000
bits<<31;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 1001 1011
//2.向左移动31位,超出32位的值被舍弃
// 1000 0000 0000 0000 0000 0000 0000 0000
bits>>3;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 1001 1011
//2.向右移动3位
// 0000 0000 0000 0000 0000 0000 0001 0011
7.2 位求反、位与、位或、位异或运算符
位求反运算符:~
逻辑:将运算对象的二进制位,全部求反。
位与运算符:&
逻辑:0&0=0,0&1=0,1&1=1
位或运算符:|
逻辑:0|0=0,0|1=1,1|1=1
位异或运算符:^
逻辑:0^0=0,0^1=1,1^1=0
unsigned char b1=0145; // 0110 0101
unsigned char b2=0257; // 1010 1111
~b1;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 0110 0101
//2.~计算
// 1111 1111 1111 1111 1111 1111 1001 1010
b1&b2;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 0110 0101
// 0000 0000 0000 0000 0000 0000 1010 1111
//2.&计算
// 0000 0000 0000 0000 0000 0000 0010 0101
b1|b2;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 0110 0101
// 0000 0000 0000 0000 0000 0000 1010 1111
//2.|计算
// 0000 0000 0000 0000 0000 0000 1110 1111
b1^b2;
//1.提升为int型32位
// 0000 0000 0000 0000 0000 0000 0110 0101
// 0000 0000 0000 0000 0000 0000 1010 1111
//2.^计算
// 0000 0000 0000 0000 0000 0000 1100 1010
7.3 位运算符总结
1.第一步,都是进行整型提升
2.位求反、位与、位或、位异或运算符都可以和赋值运算符进行复合,比如:|=,&=,等等
8. sizeof运算符
形式:1. sizeof(type) 2. sizeof expr
作用:sizeof运算符是用来求类型所占的字节数的,第一种形式是直接返回对象本身类型的大小,第二种形式是求,表达式返回的结果类型的大小,本质上都是求类型的字节数。
返回:size_t类型([c++] 数组)
Sales_data *p;
sizeof(p); //指针所占空间大小
sizeof(*p); //指针所指向的值的空间大小,即sizeof(Sales_data)
sizeof p->revenue();
int a[4];
sizeof(a); //int型大小*4
注意:
1.对解引用指针执行sizeof,得到指针所指向的对象的类型所占空间的大小,即使是无效指针,也能获取。
2.对数组执行sizeof,得到的是整个数组所占空间的大小,即数组里每个元素所占空间的和。由此可知,sizeof不会把数组换成指针来处理。所以可以通过sizeof来求得数组的容量:
int a[4];
constexpr size_t sizes=sizeof(a)/sizeof(*a);
3.对string对象和vector对象执行sizeof,只返回改类型固定部分的大小,不会计算对象中的元素占了多少空间。即如 vector<int> v1{ 0,1,2,3,4 }; sizeof(v1) 所得的值永远都是32,不会随着容器里面的元素改变而改变。