1. 操作符分类:
算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员。
2.算术操作符
+ - * / %
//当除号两端都是整数的时候,执行的是整数的除法,两端只要有一个是浮点数,执行的就是浮点数的除法。
//取模操作符两端必须是整数。
3.移位操作符 (<< 左移操作符 >> 右移操作符)
这里的位指的是二进制位。
计算机中整数有3种二进制表示形式(原码、反码、补码)
正整数的原码、反码、补码相同。
负整数的原码、反码、补码不同需要计算。
(负整数的反码=原码符号位不变,其他位按位取反;补码=反码的二进制位+1)
//负整数前面第一个位置为符号位,1表示负号。
//整数在内存中的存储的二进制形式是补码。
//打印或者使用时,用的是原码的值。
3.1 左移操作符 3.1.1正整数
方法
①将补码进行左移,将前面移出去的部分去掉,后面空出来的空间补0;
②正整数的原、反、补码相同,直接读取移位后补码的值,就得出正整数移位后的值。
3.1.2负整数
因为负整数原码、反码、补码不相同,所以负整数进行左移操作时与正整数有区别
① 先将补码进行左移,将前面移出去的部分去掉,后面空出来的空间补0;
② 将移位后的补码减1,求移位后的反码;
③ 符号位不变,其他位按位取反求出原码,通过原码可以算出 -5左移2位的值位-20,通过编译器编译得到认证。也说明在进行位操作符过程中,打印或者使用的时候。用的说原码的值。
3.2右移操作符
右移有两种形式
1.算术右移(规则:右边丢弃,左边补原来的符号位)
2.逻辑右移 (规则:右边丢弃,左边补0)
右移的形式取决于编译器,常见的编译器都是算术右移。
所以对于正整数,算术右移与逻辑右移的结果都一样。
3.2.2负整数右移
//对于移位运算符,不要移动负数位,这个是标准未定义的。
int num = 10;
num>>-1; //这是错误的。
4.位操作符(这里的位使二进制位)
//注:他们的操作数必须是整数
& - 按(二进制)位与 (全1为1,有0为0)
| - 按(二进制)位或(规则:有1为1,全0为0)
^ - 按(二进制)位异或 (规则:相同为0,相异为1)
5.赋值操作符
复合赋值
+= -= *= /= %= >>= <<= &= |= ^=
6.单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)(类型) 强制类型转换
7.关系操作符
> >= < <= != 用于测试“不相等” == 用于测试“相等
8.逻辑操作符
&& 逻辑与 事件A并且事件B
|| 逻辑或
//a后置++,先使用,在自增;a=0,对于&&前面为假时,后面不论什么情况都不用计算,整个表达式为假。
//对于逻辑或,前面为真,后面就不用计算,条件全为真。
9.条件操作符 (exp1 ? exp2 : exp3)
例子:求两个整数之间的较大数
int main() { int a = 10; int b = 20; int max = 0; if (a > b) max = a; else max = b; //条件操作符 max = (a > b ? a : b); return 0; }
10.逗号表达式 (exp1, exp2, exp3, …expN)
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
11. 下标引用、函数调用和结构成员
11.1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。11.2 ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
void test1() { printf("hehe\n"); } void test2(const char* str) { printf("%s\n", str); } int main() { test1(); //实用()作为函数调用操作符。 test2("hello");//实用()作为函数调用操作符。 return 0; }
11.3 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
12.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
12.1 隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的。
①负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
②正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
③无符号整形提升,高位补0例子:
变量a,b,c都是char类型,进行运算时需要进行整型提升。
按照整型提升的规则将a,b从1字节提升到4字节,然后相加,相加出的4字节数据需要截取最后1字节数据保存到char c中;然后需要以%d形式打印出char c 的值,继续进行整型提升,此时开头的数为1,按照规则,前面全补1,此时的补码为负数,按照负数原、反、补码的计算规则求出此时的原码,原码的值,就是以%d形式打印出char c 的值。
12.2 算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
int a =3; float b =2.3; float c = a+ b;//需要将a转换成float类型
12.3操作符的属性、
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。