1. 算术操作符
+ - * / %
注意:
%
的操作符的两个操作数都必须为整数。- 其余几个操作符皆可用于整数和浮点数。
- 对于
/
操作符,如果两个数都是整数,则执行整数除法,若有一个为浮点数,则执行浮点数除法。
2.移位操作符
- 逻辑移位:二进制位上进行左移右移,两头用0进行补充,不用关心符号位的问题。
- 算术移位:二进制位上进行左移右移,两头用0进行补充,但必须保证其符号位不变。
介绍4个汇编中的指令:
SHL:逻辑左移
SHR:逻辑右移
SAL:算术左移
SAR:算术右移
int main()
{
char i = 128;
i = i >> 3;
printf("%d\n", i);
return 0;
}
上述代码块究竟采用的是逻辑右移呢还是算术右移?
1. 有符号整型正数左移
int main()
{
int i = 1;
i = i << 3;
return 0;
}
我这里采用的是vs2017,打开反汇编窗口:
采用的为逻辑左移。
2. 无符号整型正数左移
int main()
{
unsigned int i = 1;
i = i << 3;
return 0;
}
采用的为逻辑左移。
3. 有符号整型负数左移
int main()
{
int i = -1;
i = i << 3;
return 0;
}
采用的为逻辑左移。
4. 无符号整型负数左移
int main()
{
unsigned int i = -1;
i = i << 3;
return 0;
}
采用的为逻辑左移。
结论:无论有无符号类型,正数负数,左移时均为逻辑左移。
5. 有符号整型正数右移
int main()
{
int i = 1;
i = i >> 3;
return 0;
}
采用的算术右移。
6. 无符号整形正数右移
int main()
{
unsigned int i = 1;
i = i >> 3;
return 0;
}
采用的为逻辑右移。
7. 有符号整型负数右移
int main()
{
int i = -1;
i = i >> 3;
return 0;
采用的为算术右移。
8. 无符号整型负数右移
int main()
{
unsigned int i = -1;
i = i >> 3;
return 0;
}
采用的为逻辑右移。
结论:有符号数,在右移时采用算术右移;无符号数,在右移时采用逻辑右移。
注意:在这种情况下,虽然为无符号数,但是最高位为1,被当成了有符号数,采用的为算术右移,但右移时,左边补0。
int main()
{
unsigned char i = 128;
i = i >> 3;
printf("%d", i);
return 0;
}
结果为:
为什么左移全部是逻辑左移呢?
答:先了解不同数在二进制中的存储方式。
正数按照原二进制存储。如3,在计算机中存储为:0x3;
负数按照其补码方式在计算机中存储,如-3,在计算机中存储为0xfffffffb。
计算机以补码的形式存储负数。有无符号数在左移时,均不会改变其符号位,故采用的均为逻辑左移;但在右移时,无符号数右移不会改变其符号位,有符号数必须保证其符号位保持不变,故只能采取算术右移。
左移相当于乘2
右移相当于除2
左移右移不会改变其本身的值
直接移位比乘除好,更快一点
3.位操作符
& //按位与
| //按位或
^ //按位异或
他们的操作数必须为整数。
一道比较有意思的题:求一个整数存储在内存中的二进制1的个数?
- 一般的思路是,做循环,将该数挨个右移,并和1&,计数。这里面的做法必须循环32次,可以进行优化。先附这样的代码:
int main()
{
int num = 99;
int i = 0;
int count = 0;//计数
for (; i < 32; i++)
{
if (1 == ((num >> i) & 1))
{
count++;
}
}
printf("%d\n", count);
return 0;
}
2.另一个种比较难想的思路:做循环,令该数等于该数&
该数减1。
第一次:计数+1
第二次:计数+1
第三次:计数+1
第四次:计数+1
最后该数为0,循环停止,该数二进制总共有4个1。
附代码:
int main()
{
int num = 99;
int count = 0;//计数
while (num)
{
count++;
num = num & (num - 1);
}
printf("%d\n", count);
return 0;
}
结果为:
4.赋值操作符
- 复合操作符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
注:这样的连等式子都是从右向左看。
如:
int main()
{
int a = 3;
int i = 10;
int j = 20;
a *= i + j;
printf("%d\n", a);
return 0;
}
输出结果为:90。
5.单目操作符
! 逻辑反操作
-
+
& 取地址
sizeof 操作数的类型长度(字节为单位)(不是函数)
~ 按二进制位进行取反
- -
++
*
(类型) 强制类型转换
笔记:任何一个变量都可以站在3个角度去理解:变量空间、变量内容、变量地址。
强转改变的是我们看待数据的方式。
类型决定了:变量的意义,看待的方式,开辟了多大的空间。
6.关系操作符
>
>=
<=
!=
==
7.逻辑操作符
&&
||
注意:
逻辑与:前半部分不成立,则不用运行后边的。
逻辑或:前半部分成立,则不用运行后边的。
8.条件操作符
也叫做三目运算符
如a>b?a:b
,意思为如果a大于b,则输出a,否则输出b。
9.逗号表达式
在逗号表达式中,会依次执行逗号中的内容,并将最后一个表达式作为该式子的值。
int main()
{
int b = (1, 2, 3, 4, 5, 6, 7, 8);
printf("%d\n", b);
return 0;
}
最后结果为:b=8。
操作符的优先级和结合性。
一般不要写特别复杂的表达式。
隐式类型转换:
为什么要整型提升?
因为表达式的运算是在CPU的的相应运算器件中执行的,CPU整型运算器(ALU)的操作数的字节长度一般是int的字节长度,同时也是CPU的通用寄存器长度。
如何进行整型提升?
整型提升是按照变量的数据类型的符号位来提升的。
①如果为无符号数:整型提升直接全部补0。
②如果为有符号数:补符号位(看变量的类型)。
一个数为char类型,与操作符进行运算时就会发生整型提升。