目录
# 运算符与表达式
C语言提供了所有你希望编程语言应该拥有的操作符,他甚至提供一些你意想不到的操作符。
事实上,C语言之所以被很多人认为它很难精通就是因为它品种繁多的操作符。
它的这个特点也是其他语言无法抗衡价值
## 什么是运算符
顾名思义,就是说运算符用来表示某种运算的符号
几目运算符?表示这个运算符要带几个操作数
单目运算符: 该运算符只需要一个操作数 eg:++ --
双目运算符: 该运算符只需要两个操作数 eg:+ - *
三目运算符: 该运算符只需要三个操作数 eg:a>b?a:b
结合性: 决定先算哪个操作数的问题
从左到右 还有 从右到左
eg:
a+b
b+a
在C语言中,含义是不一样的
+ 结合性: 从左到右
eg:
int i =5 , j = 6;
int a;
a = (i++) + (i+j); //17
int i =5 , j = 6;
int b;
b = (i+j) + (i++); //16
int i =5 , j = 6;
int a;
int b;
a = (i++) + (i+j); //17
b = (i+j) + (i++); //18
int i =5 , j = 6;
int a;
int b;
a = (++i) + (i+j); //18
b = (i+j) + (++i); //19
i++ ====> i = i+1
运算符的优先级:
在含有多个运算符的表达式中,决定先算哪个运算符,后算哪个运算符的问题
eg:
a+b*c
单目运算符 > 算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符
## 什么是表达式
表达式就是表达某个意思的式子
在C语言中,一般来说,表达式是指用运算符连接操作数的式子
3+5
eg:
sb250
i love you
520
合法的表达式就一定会有一个值,因为任何表达式都需要表达某个意思
## 运算符的分类
### 算术运算符
算术运算符:进行算术运算的运算符
++ -- : 单目运算符
+ - * / % : 双目运算符
+ - * / : 只需要你的操作数是一个数(整数或者小数都可以)
% : 要求操作数必须是整数
++ -- : 小数,浮点型
eg:
3+5 = 8
double + int ===》 double
3.0 + 5 = 8.0
int a,b;
a + b;
double f1 , f2;
f1+f2;
3.5 % 3 ==== error
% 表示取余
5/4 == 1
5.0 / 4 = 1.25
double / int ===> double / double = double
(double)(3/2) = 1.0
(double)3/2 = 1.5
3/2.0 = 1.5
用C语言的表达式来表达数学表达式 a/b
(double) a/b
1.0*a / b
% 求余数
5 % 4 === 1
6 % 7 === 6
++(自增)运算符,单目运算符,要求操作必须是一个左值
--(自减)运算符,单目运算符,要求操作必须是一个左值
### 左值 和 右值
在这里介绍一下
为了理解一些操作数存在的限制,你必须要理解左边和右值之间区别。
左值就是赋符号(=)左边的东西,右值就是赋值符号右边的东西
eg:
a = b + 25;
a 就是 左值, b+25 就是右值
他们两个可以互换吗??
b + 25 = a;
原先用作左边的a此时变成了右值,因为每个位置它是不是肯定包含一个值,然后 b+25 不能作为一个左值,
因为它没有表示一个特定位置,因此,这个语句是非法!
注意当计算机去计算 b+25 的时候,它的结果必然保存在机器的某个位置,但是我们作为程序员没有办法来预测到这个位置在什么地方!
5++; error i++ === > i = i+1
(a+b)++; error
表达式的值 做完表达式后的值
i++ i i+1
++i i+1 i+1
i-- i i-1
--i i-1 i-1
### 关系运算符
这一类运算符用于测试操作数之间的各种关系的,c语言提供所有常见的关系运算符
不过,这一组操作符里面有一个大大的陷阱
这一组操作符:
> >= < <= != ==
前面四个操作符的功能你一看就知道是什么意思??
!= 操作符用来测试 ”不等于“
而== 操作符用于测试”等于“
关系表达式:用关系运算符连接起来的式子,如: a < b 8 < 7
关系表达式的值:
关系成立 : 1
关系不成立 : 0
eg:
5 > 4 1
3 <= 4 1
5 > 7 0
3+5 < 7*8
(3+5) < (7*8)
8 < 56 ----> 1
5 > 4 > 3 这个东西是一个合法的关系表达式
1 > 3 ----> 0
在C语言中, 5>4>3 这个表达式和数学5>4>3的表达式不一样
(1)、在C语言中,5>4>3 ===》 1>3
(2)、数学上,5>4>3
5>3 并且 4>3
ps:
绝大部分其他的语言使用 = 操作符来进行比较 相等性 。在C语言中,你必须要用== 来进行比较,单个= 是赋值
这个的陷阱在于: 在测试相等性的时候出现赋值符号也是合法的!! 它不是一个语法错误
if(a==b)
if(a=b)
### 逻辑运算符
! 逻辑非 单目运算符 ”取反“
&& 逻辑与 双目运算符 ”并且“ 从左到右
|| 逻辑或 双目运算符 ”或者“ 从左到右
逻辑表达式:用逻辑运算符连接起来的式子,逻辑表达式
逻辑表达式的值:
逻辑为真: 1 (非0)
逻辑为假: 0
eg:
int a = 4 , b = 5;
a&&b; ===== 1
a||b; ===== 1
!a||b; ===== 1
!a&&b; ===== 0
5>3 && 8 < 4 - !0
5>3 && 8 < 3
1 && 0 =====> 0
ps. C语言运算符是”惰性运算“
(1)、a&&b&&c
只有当a为真的时候,才会去判断b
同时a和b都为真的时候,才会去判断c
(2)a||b||c
只要当a为真的时候,那么就不会再去判断b和c的值了
只有当a 为假的时候,才需要去判断b 的值
当b 为假的时候,才需要去判断c 的值
### 位运算符
位运算符是按bit为展开来进行运算,位运算符要求操作数必须是一个整数(兼容的整数),因为在进行位运算时,需要把操作数按bit展开。”按每一个bit位来进行操作的“
& 按位与
| 按位或
^ 按位异或
~ 按位取反
>> 按位右移
<< 按位左移
除了 ~ 是单目运算符之外,其他的位运算符全部是双目运算符,结合性是从左到右的,位运算的操作数只能是整型。所有的位运算都需要把操作数变成bit位序列,然后进行操作
#### 按位取反 ~
1 ---》 0
0 ---》 1
~3:
0000 0011
1111 1100
##### 例题
int a = ~(-3);
-3:
0000 0011
1111 1100
1111 1101
~ 0000 0010 ====>2
printf("%d\n",a); //2
printf("%u\n",a); //2
int a = !(-3)
printf("%d\n",a); //0
printf("%u\n",a); //0
!(非0) ==== 0
NOTE:
要注意 ~(按位取反) 和 !(逻辑非) 的区别
!(3+5) ==== 0
~ (3+5) ==== ~8
#### 按位与 &
a & b
a b a&b
1 1 1
1 0 0
0 1 0
0 0 0
& 如果两个bit位都为1,结果才为1,否则为0
eg:
3 & 5
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000101
00000000 00000000 00000000 00000001 =====》1
3 & 7
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000111
00000000 00000000 00000000 00000011 ======》3
3&&5 ====》 1
5&&7 ====》1
NOTE:
注意&(按位与)与&&(逻辑与)的区别
ps.
一个bit位 与 0 进行按位与&操作,结果为0
X & 0 ==== 0
when x == 1 , 1&0 == 0
when x == 0 , 0&0 == 0
一个bit位 与 1 进行按位与&操作,结果保留原来的值
X & 1 == X
when x ==1 , 1 & 1 == 1;
when x ==0 , 0 & 1 == 0;
#### 按位或 |
a | b
a b a|b
1 1 1
1 0 1
0 1 1
0 0 0
按位或,只要有一个bit操作数为1,结果就为1
3|5 === 7
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000101
00000000 00000000 00000000 00000111 ====》7
3||5 == 1
NOTE:
要区分 | 按位或 和 || 逻辑或的区别
一个bit位 与 1 进行 ”按位或|“的一个操作,结果为1
X | 1 ==== 1
一个bit位 与 0 进行 ”按位或|“的一个操作,结果保留原来的值
X | 0 ==== X
#### 按位异或 ^
"异或":求异,不同为1 ,相同就为0
a ^ b
a b a^b
1 1 0
1 0 1
0 1 1
0 0 0
eg:
3^5 === 6
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000101
00000000 00000000 00000000 00000110 ===>6
ps:
一个 bit 位 与 0 进行”按位异或 ^“,保留原值
x ^ 0 === x
一个 bit 位 与 1 进行”按位异或 ^“,结果相反
x ^ 1 === ~x
#### 按位右移
X >> N 把 x 按照bit位整体往右边移动n位!
低位右移后,舍弃
0000 1111 ----> xxxx 0000 (右移四位)
高位补什么?? xxxx是什么??
对于无符号数,高位就全部补0
对于有符号数,高位就全部补原来的符号位
#### 按位左移
双目运算符,按bit位左移
2 << 1 ===> 4 0000 0010 ===> 0000 0100
高位左移,舍弃,低位就补0
如果左移动舍弃的高位是0,那么左移n位,表示的结果就乘以2的n次方
### 赋值运算符 =
双目运算符 , 结合性从右到左
x = y;
把表达式y的值,赋值给x(隐含了一个条件 x必须是一个可写的地址,具备左值,一般来说,X为一个可变的数据对象 ”变量“)
赋值表达式的左边(左操作数)必须为一个”可写的地址“ 左值
5 = 5; error
2 + 3 = 5; error
int i = 5;
i++ = 6; error
i = 6;
赋值表达式:是由赋值运算符连接起来的式子,赋值表达式
赋值表达式的值:就是你赋值后左边那个操作数的值
i = 5;
a = i = 5;
====>
a = (i = 5);
复合的赋值运算符 : 赋值运算符可以和算术运算符,位运算符复合的赋值运算符
+= -= *= /= %= >>= <<= != |= &= ^=
a+=b ====> a = a + b;
a >>= 2 ====> a = a >> 2;
a = a+b ; ===> a+=b;
### 条件运算符 ? :
三目运算符 结合性从右到左
experion ? a : b
上面这个是一个条件表达式
如果experion的值是为真,则整个条件表达式的值就为a
如果experion的值是为假,则整个条件表达式的值就为b
eg:
if(a>b)
{
a = 250;
}
else
{
a = 520;
}
a = (a>b) ? 250 : 520
a = (a>b) ? (a = 250) : (a = 520)
### 逗号表达式
双目运算符,优先级就是最低的,结合性从左到右
表达式1 ,表达式2 ,表达式3 ......
逗号表达式的求值顺序:先求表达式1的值,再求表达式2的值,再求表达式3的值
整个逗号表达式的值就是表达式3的值
eg:
int a = 5 , b =6;
a = (a = 6 , a + b );
逗号表达式的扩展形式:
表达式1 , 表达式2 , 表达式3 ,.......,表达式n
求值顺序:
先求1,2,3,4,5,最后整个逗号表达式的值,是不是就是表达式n的值
### sizeof()求字节运算符
sizeof(类型); 求出这个类型的字节数
sizeof(typeof(1));
sizeof(typeof(1.0));
### 分量运算符
求结构体的成员变量 . ->
等后面结构体的专题再讲
### 下标运算符
int a[10];
a[0];
a[1];
a[2];
[]
.....
后面数组的时候也会涉及
### 强制类型转换的运算符
语法:(类型)值
eg:
(int)3.5 ===>3
typeof((int)3.5) ===> int
### 其他运算符
如:
函数调用符()
## 总结
### 运算符的优先级和结合性
= 不同于 ==
由Algol派生而来的大多数程序设计语言,例如:Ada,使用:== 作为赋值运算符,但是符号 = 作为比较运算符,而我们的C使用了另外一种表示方法,符号 = 作为赋值,== 作为比较!
一般来说,赋值运算符肯定是比 比较运算符,出现次数更加多,因此字符较少的 = 作为了赋值运算符
该语句的本意应该是检查x是否等于y
if(x = y)
{
...
break;
}
实际上将 y赋值给了x
&和| 不同于 && 和 ||
### 运算符优先级
我们需要记住两句话:
1、任何一个逻辑运算符的优先级低于任何一个关系运算符
2、移位运算符的优先级要比算术运算符要低,但是要比关系运算符要高