运算符(操作符)!

在C语言中,运算符也是极为重要的知识点之一,C语言的运算异常丰富,除了 控制语句 和 输入输出 以外的几乎所有的基本操作都为运算符处理,下面我就来总结一下这一星期对于运算符的学习。

1. 算数运算符

+ - * / %

①:+ 算术加 (正数) 333 + +322;
int + int 结果是 int 类型
(要注意数据溢出)
②:- 算术减 (负数)
③:* 算术乘
④:/ 算术除
对于int类型来说 整除 int/int 结果只会是 int
float f = 10/4; // 结果为 2.00 除数不能为0 但允许是0.0
⑤:% 取余运算符
取余两边的操作数必须都是 整数 (不可以是浮点数,可以是char,short,int,long…)
任何数据不能对0取余(%右边不能为0)

注意:
(1) 整数字面值如果是在 [-2^31,2^31-1]    默认是int类型
      int类型数据运算时得到的结果为int类型
         int + int  ==>   int
         long long + long long  ==>  long long
 (2) 不同的数据类型进行运算
       窄字节数据和宽字节数据进行运算时,窄字节数据会隐式转换为宽字节数据
     (低字节的数可以隐式转换成高字节的数据)  
         对于有符号的数扩充 符号位 
         对于无符号的数扩充 0
(3) unsigned 和 signed类型数据进行运算时,把 signed 数据转换为 unsigned
       (注意:考试时 一定会考这个知识点)
例如: int a = -16;
 unsigned int b = 10;
if(a+b>=0){// 恒成立的  int+unsigned 结果为 unsigned
}
2. 自增减运算符

++ --

① 前加加 ++a
b = ++a; 本质上等于:a=a+1; b=a; 操作数先自增,然后再拿自增之后的结果计算表达式
② 后加加 a++
b = a++; 本质上等于:b=a;a=a+1; 先计算表达式的结果,然后操作数自增
如果是++a; a++;这样两个表达式没有区别的,甚至a++会被编译器优化成++a;
+++a; ++++a; 错误的
a+++b;//错误的
a + ++b; //正确的 三个加号 必须有空格
注意:
不要在同一个表达式中对同一个操作数进行多次自增减运算
不同的编译器或者操作平台会有不同的解释
不要在调用函数是对实参进行自增减运算
③ 前减减 --a
④ 后减减 a–

注意:
(1)自增减运算符的操作数必须是左值
(2)操作数可以是任意类型,但一般都用于整数类型
3. 关系运算符(比较运算符)

< <= > => == !=

① 关系运算符的结果只有两个 0 或者 1 ,代表 成立 或者 不成立
② 在c语言中,一个 = 是赋值语句 , 两个 = 才是判断是否相等(比较运算符)
③ 关系运算符如果出现在同一个表达式中,会从左往右计算每一个关系运算符的结果

int a = 3;
   int f = 1<a<2 ;   //恒为1
    //1<a  要么是0,要么是1  ; [0,1] < 2   恒成立
4. 逻辑运算符

&& || !

逻辑运算符用于连接多个逻辑值 或者 关系表达式的,最终表达式为 1 或 0 , 真为1 假为0
例如:if(1<a && a<5) 才是表示 a在(1,5)区间
① && :逻辑与
a b a&&b
真 真 真
真 假 假
假 真 假
假 假 假
② || :逻辑或
a b a&&b
真 真 真
真 假 真
假 真 真
假 假 假
③ ! :逻辑非
a !a
真 假
假 真

短路特性:
  && 连接的两个表达式,如果 && 前面表达式的结果为假,则后面的表达式不再计算,结果为 0
  || 连接的两个表达式,如果 || 前面表达式的结果为真,则后面的表达式不再计算,结果为 1
    因为 假&&x  , 真||x   ,此时无论x的结果如何都与最终结果无关,此时为了效率着想就不计算了
注意:   
 && 的优先级高于 || 的优先级
int a = 0,b = 0,c = 0;
   int x = ++a>0||b++>0&&++c>0;   // 相当于 x = ++a>0||(b++>0&&++c>0)
   a = 0,b = 0,c = 0;
   x = a++>0&&++b>0||++c>0;      // 相当于 x = (a++>0&&++b>0)||++c>0
###
5. 位运算符

& | ^ ~ >> <<

位运算符是指对数据按二进制位进行运算
(不适用于浮点类型)

① & 按位与
如果有二进制位 0和1 则
1&1 = 1
1&0 = 0
0&1 = 0
0&0 = 0
推论:

num&-1 = num
num&0 = 0
判断一个数是否为偶数:
num%2 == 0 或
num&1 == 0

二进制最低位如果是1是奇数,如果是0是偶数

② | 按位或
如果有二进制 0 和 1
1|1 = 1
1|0 = 1
0|1 = 1
0|0 = 0
推论:

num|-1 = -1
num|0 = num

③ ^ 按位异或(异1:不同则为1)
如果有二进制位 0 和1
1^1 = 0
1^0 = 1
0^1 = 1
0^0 = 0
推论:

num^num = 0
num^-1 = -num = -(num+1)
num^0 = num

//交换两个整数的值:
  int a = 10;
  int b = 20;
  a=a^b;
  b=a^b;
  a=a^b;

~ 按位取反
如果有二进制位 0 和 1
~0 = 1
~1 = 0
**推论: **

** ~num = -(num+1)**
负数在计算机中是以补码形式存在,等于其正数按位取反+1

⑤ >> 按位右移
1: 右边移出的位舍弃,左边空出的位补(对于有符号的数补符号位,无符号的数补0)
2: 每向右移一位相当于除以一次2 (-1除外,-1右移还是-1)
3: 向右移的位数仅限于 [0,31] ,超出范围会对32求余

⑥ << 按位左移
左边移出的位舍弃,右边空出的位补0
每向右移一位相当于乘以一次2

6. 赋值运算符

= 混合赋值运算符 += -= *= /= %= &= |= ^= >>= <<=

混合赋值运算符中 除了 = 剩下的那个运算符必须是双目运算(有两个操作数)
例如:a = b; 等同于 a = ab;
a = 10;b = 10;
a = b += 20;

7. 三目运算符

expr ? expr1 : expr2

expr 结果如果为真,取前面那个值expr1
结果如果为假,取后面那个值expr2

8. sizeof (重点)
(1) sizeof是操作符,不是一个函数  
      sizeof求变量、数据类型、表达式结果所占内存的字节大小 
(2) sizeof后面可以不加括号(),但如果操作数是类型的话一定得加上括号()   
      sizeof a      sizeof(++a)
(3) sizeof只关心表达式结果类型,并不会计算表达式
(4) 两个字符相加 'a' + 'a' ==> 结果类型为 int
(5) sizeof(expr)expr是一个多类型表达式时,只关心最宽的字节类型
(6) sizeof (函数名)  函数名没有小括号 ,始终为 1
(7) sizeof (函数名())函数名有小括号 , 结果等于 函数数返回值类型的字节宽度
(8) sizeof(指针)  结果为 4    
      指针 == 内存地址
9. 其他

[] *(取值) & . -> , ()

① ,逗号运算符: 分隔 一条语句中的多个表达式,多个表达式都会从左往右逐一计算
② . 结构体 或者 联合 访问成员运算变量符
③ -> 结构体 或者 联合 指针 访问成员运算符
④ [] 数组下标访问运算符
⑤ * 指针取值 运算符
⑥ & 变量取址 运算符
⑦ () 优先级符

注意:
(1):所有数据都是有字节宽度大小的,在赋值在运算过程中,都可以发生变化
(2):隐式类型转换
     char --> int --> long --> double
     float --> double
     short --> int
     signed --> unsigned
(3):运算符优先级:
       算术运算符 > 位运算符 (~除外)
       算术运算符 > 关系运算符 > 逻辑运算符
(4):signed + unsigned ---> unsigned