1. 算数操作符
- 算数操作符包括+、-、*、/
- 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
- % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
2. 移位操作符
<< 左移 >> 右移
移位操作符针对的是整数的二进制位
移位规则:
右移运算分两种:
- 逻辑移位
左边用0填充,右边丢弃 - 算术移位
左边用原该值的符号位填充,右边丢弃
一般编译器采用的都是算数右移,算数右移也更符合规则
3. 位操作符
& //按位与
| //按位或
^ //按位异或
不能创建临时变量(第三个变量),实现两个数的交换。
// 采用异或的方法
//3^3 = 0 -> a^a=0
//011
//011
//000
//0^5=5 -> 0^a = a
//000
//101
//101
//
//3^3^5 = 5
//3^5^3 = 5
//异或操作符支持交换律
//011
//101
//110
//011
//101
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d b=%d\n", a, b);
a = a ^ b;//a=3^5
b = a ^ b;//3^5^5 --> b=3
a = a ^ b;//3^5^3 --> a=5
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
通常交换两个变量的值采用创建临时变量的方法,更加高效(但这种方法可能有溢出的风险)
4. 赋值操作符 =
==关系操作符和=赋值操作符不能混淆
5. 单目操作符
5.1 !逻辑反操作
int main()
{
int flag = 3;
//flag为真,进入if
if (flag)
{}
//flag为假,进入if
if(!flag)
{}
return 0;
}
在C语言未引入bool类型时,用!(逻辑反)来表示真假
5.2 sizeof 操作符
#include <stdio.h>
int main()
{
int a = -10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);//这样写行不行?
printf("%d\n", sizeof int);//这样写行不行?
return 0;
}
sizeof a 可以,但sizeof int 不行 进而说明sizeof并不是函数,函数是一定要带括号的
而strlen是库函数,是用来求字符串长度
5.3 ~ 按位取反操作符
int a = 0;
//~ 是按二进制位取反
//00000000000000000000000000000000 - 补码
//11111111111111111111111111111111 -> ~a
//11111111111111111111111111111110
//10000000000000000000000000000001
//-1
printf("%d\n", ~a);//-1
int a = 3;
//00000000000000000000000000000011
//11111111111111111111111111111100 - 补码
//11111111111111111111111111111011
//10000000000000000000000000000100
//-4
按二进制取反后,因为计算机存储的形式是补码,而在控制台显示的都是原码形式
5.4 前置++,后置++
int a = 3;
//int b = ++a;//前置++,先++,后使用
//a=a+1,b=a;
int b = a++;//后置++,先使用,再++
//b=a, a=a+1
printf("%d\n", a);//
printf("%d\n", b);//
int a = 3;
int b = a--;
//b=a,a=a-1
//int b = --a;//前置--,先--,后使用
//a=a-1,b=a
printf("%d\n", a);//2
printf("%d\n", b);//3
这里的使用指的是给表达式赋值,在函数传参时若是前置先++,再传过去;若是后置++,先传过去再++;
6.关系操作符
>= <= ==...
不是所有类型判断相等都是采用==,两个字符串判断相等应该用strcmp
7.逻辑操作符
逻辑操作符包括 逻辑或|| 逻辑与&& 存在控制求值顺序
&& 左边为假,右边就不计算了 || 左边为真,右边就不计算了
8. 条件操作符
若表达式1为真,返回表达式2的值,表达式3不计算
9.逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
10. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。
arr[9]的形式还可以写成9[arr] arr[9] 其实就是*( arr + 9 )
=> *(9+arr)
=> 9[arr]
11. 表达式求值
11.1 隐式类型转换
为了获得缺省整型(int)精度,表达式中的字符(char)和短整型(short)操作数在使用之前被转换为普通(int)整型,这种转换称为整型提升。
int main()
{
char a = 5;
//00000000000000000000000000000101
//00000101 - a
char b = 126;
//00000000000000000000000001111110
//01111110 - b
char c = a + b;
//00000000000000000000000000000101 - a
//00000000000000000000000001111110 - b
//00000000000000000000000010000011
//10000011 - c
//11111111111111111111111110000011 - 补码
//11111111111111111111111110000010
//10000000000000000000000001111101
//
printf("%d\n", c);//-125
return 0;
}
char、short类型只要出现在表达式当中就会出现整型提升的现象
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
return 0;
}
上述代码只有c打印成功,a,b都发生整型提升,最高位为1 整型提升补1(也就是负数)肯定不相等
11.2 算术转换(强制类型转换)
如果在表达式当中出现不同类型的变量,首先要转换为更高的操作数类型后执行运算。
12. 操作符的属性
后缀++的优先级高于+,+的优先级高于前缀++
复杂表达式的求值有三个影响的因素。
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
尽管知道了两个操作符的优先性与结合性,有时候还是无法得到唯一结果
代码中的变量是static静态的,无法确定那个fun()是先执行的,这就产生了歧义
总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题
的。(尽量避免)