1 运算符
什么运算符?
运算符是用来表示某种运算(操作)的符号.
如 : + - * /
几目运算符? 表示这个运算符需要几个操作数(运算数)
单目运算符 : 该运算符只需要一个操作数, 如 : ++ , -- &
双目运算符 : 该运算符需要两个操作数, 如 : + - * /
三目运算符 : 该运算符需要三个操作数, 如 : ? :
结合性 : 决定先算哪个操作数的问题.
从左到右结合, 从右到左
举个例子 :
已知 + 结合性 "从左到右" 结合,在C语言中 :
a + b 和 b + a 含义是不一样的.
运算符的优先级
在含有多个运算符的表达式中,决定先算哪个运算符,后算哪个运算符的问题.
如 : a + b*c
==单目运算符 > 算数运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符==
2 表达式
什么是表达式?
表达式是表达某种意思的式子.
C语言中的表达式 ,一般来说用运算符连接操作数的式子 , 如 : 3 + 5
表示式的值
是一个表达式,就一定会有一个值,这个值就是表达式的值.
表达式最终需要表达某个意思,某个意思就是表达式的值.
int a = 3 + 5; 这是一条语句,表达式加上一个 ; 就是一条语句.
3 运算符分类
3.1 算数运算符
进行算数运算的运算符
-
++ -- 单目运算符
-
+ - * / % 双目运算符
/ 只需要操作数是一个数(整数,也可以是小数) 就行
% 求余数,要求两个操作数都必须为整数
如 :
3 + 4 => 7 3 + 4.0 => 7.0 4/2 => 2 4/2.0 => 2.0 3/2 =>1 3/2.0 => 1.5 3.5 % 2 error 3 % 2 => 1 -3 % 2 (超出知识范围) -3 % -2 (double)(3/2) => 1.0 (double)3/2 => 1.5 用c语言的表达式 来描述数学表达式 a/b 1.0*a/b
++ (自增运算符)
自增(+1)运算符,单目运算符,要求操作数必须为一个左值而且是一个整数(整型变量)
5++ //error (a + b)++ //error 表示式一定会有一个值 int x; x++; //可以的 ++x; //可以的 char y; y++; //可以的 前置++ : ++ 在前面 如 : ++x 后置++ : ++ 在后面 如 : x++
-- (自减运算符)
自减(-1)运算符,单目运算符,要求操作数必须为一个左值而且是一个整数(整型变量)
前置 -- : -- 在前面 ,如 : --x 后置 -- : -- 在后面 ,如 : x-- 表达式的值 做完表达式后,i的值 i++ i i = i + 1 ++i i+1 i = i + 1 i-- i i = i - 1 --i i-1 i = i - 1
例子 :
int main() { int i = 5,j = 6; int a; a = (i++) + (i + j); printf("a = %d\n",a);//17 return 0; } int main() { int i = 5,j = 6; int b; b = (++i) + (i + j); printf("b = %d\n",b);//18 return 0; }
3.2 关系运算符
用来判断两个东东的关系的运算符."关系" : 数值的大小关系
双目运算符, 结合性从左到右
< <= > >= == !=
关系表达式 : 用关系运算符连接起来的式子. 如 : a < b
关系表达式的值 :
关系成立 1
关系不成立 0
如 :
3 > 5 表达式的值 0
5 > 4 表示式的值 1
5 > 4 > 3 表达式的值是 0
结合性从左到右
==> (5 > 4) > 3
==> 1 > 3
==> 0
5 > 4 > 3 是一个合法的表达式,先拿表达式 5 > 4 的值 和 3进行比较
在C语言中 , 5 > 4 > 3 表达式 和 数学上 5 > 4 > 3 的含义不一样
(1) C语言中, 5 > 4 > 3 <==> (5 > 4) > 3
(2) 数学中, 5 > 4 > 3 是 "5 > 4 并且 4 > 3"
3.3 逻辑运算符
! 逻辑非 单目运算符 "取反"
&& 逻辑与 双目运算符 "并且",从左到右
|| 逻辑或 双目运算符 "或者",从左到右
逻辑表达式:用逻辑运算符(! && ||)连接起来的式子.
逻辑表达式的值:
逻辑真 1(非0)
逻辑假 0
例子 :
int a = 4,b = 5;
a && b ==> 1
a || b ==> 1
!a && b ==> 0 && b ==> 0
5 > 3 && 8 < 4 - !0
5 > 3 && 8 < 4 - 1
5 > 3 && 8 < 3
1 && 0
==> 0
练习 :
1.分析如下程序的输出结果
int main()
{
int a,b,c,d,m,n;
a = 1;
b = 2;
c = 3;
d = 4;
m = 1;
n = 1;
( m = a > b ) && (n = c > d);
printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,m,n);
return 0;
}
C 语言运算符是 "惰性运算符"
(1) a && b && c
只有a为真时,才需要判断b的值
只有a 和 b 都为真时,才需要去判断c的值
(2) a || b || c
只要a 为真 ,就不必判断b 和 c 的值了
只有a 和 b 都为假的时,才需要判断c的值
总结 : 如果事先知道表达式的值,那么后面的运算符或表达式就不执行.这就是C语言运算符的"惰性''.
3.4 位运算符
位运算符是按bit位展开来进行运算.意思是说,操作数展开成bit位,然后在进行运算.
位运算符要求操作数必须为整数(兼容的整数类型)
& 按位与
| 按位或
^ 按位异或
~ 按位取反
<< 按位左移
>> 按位右移
除了~(按位取反)是单目运算符,其他的都是双目运算符,结合性从左到右
(1) ~ 按位取反
1 -> 0
0 -> 1
~3 :
00000000 00000000 00000000 00000011
11111111 11111111 11111111 11111100 <- ~3
printf("%d\n",~3);//-4
==注意:==
按位取反~ 和 逻辑非 ! 的区别
!x ~x 两个都是单目运算符
~ 要求操作数是整数
! 对x无要求
(2) & 按位与
a & b , 双目运算符 , 结合性从左到右
a b a&b
1 1 1
1 0 0
0 1 0
0 0 0
& 按位与 如果两个bit位都为1,结果才为1,否则 0
例子 :
3 & 5 = ? //1
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000101 &
00000000 00000000 00000000 00000001
3 & 7 = ? //3
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000111 &
00000000 00000000 00000000 00000011
#include <stdio.h>
int main()
{
printf("%d\n",3&7);//3
printf("%d\n",3&&7);//1
int a = 3.5 & 3.7;//error,位运算符要去 操作数是 整数类型
int b = 3.5 && 4.7;//可以的
return 0;
}
==注意:==
按位与 & 和 逻辑的区别
练习 :
假设有一个整型变量a,要把a的第5 bit,变为0,其他bit位不变,该如何去操作.
a = a & ~(1<<5)
结论 :
-
一个bit位 与 0 进行 ''按位与 & '' 操作,结果 0
x & 0 => 0
-
一个bit 位 与 1 进行"按位与 &"操作 ,结果为 x
x & 1 => x
(3) 按位或 |
a | b 双目运算符 , 结合性从左到右
a b a|b
1 1 1
1 0 1
0 1 1
0 0 0
按位或 ,只要有一个bit位操作数为1,其结果就是 1
例子 :
3 | 7 = ?//7
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000111 |
00000000 00000000 00000000 00000111
3 || 7 = ? //1
练习 :
假设有一个整型变量a,要使a的第5bit置1,其他bit位不变,该如何操作?
a = a | (1 << 5)
结论 :
-
一个bit位 与 1 "按位或 | "操作 ,结果 1
x | 1 => 1
-
一个bit位与0进行"按位或 | "操作 ,结果 为 x (保留原值)
x | 0 => x
(4) 按位异或 ^
"异或" : 求异, 不同为1, 相同为 0
a ^ b ,双目运算符 ,结合性从左到右
a b a^b
1 1 0
1 0 1
0 1 1
0 0 0
例子 :
3 ^ 6 = ? //5
00000000 00000000 00000000 00000011 3
00000000 00000000 00000000 00000110 6
00000000 00000000 00000000 00000101 5
练习 :
假设有一个整型变量a ,要使a的第5bit保留,其他位取反,该如何操作?
a = a ^ ~(1 << 5)
结论 :
-
一个bit 位与 0 进行"按位异或 ^ " 操作 ,结果为 x (保留原值)
x ^ 0 => x
证明 :
when x = 1,1 ^ 0 = 1
when x = 0,0^0 = 0
-
一个bit位与1进行"按位异或 ^ "操作 ,结果 ~x(取反)
x ^ 1 => ~x
证明 :
when x = 1,1^1 = 0
when x = 0, 0^1= 1
(5) 按位左移 <<
a << n 双目运算符,结合性从左到右, "把 a 按bit位整体左移 n 位"
高位左移后, 舍弃, 低位空出 n 位,直接补 0;
如果左移舍弃的高位都为 0, 那么左移 n 位, 就表示 在原值乘以 2 的 n 次方.
例子 :
int a = 8 << 2; 00000000 000000000 00000000 0000 1000 <<2 00000000 000000000 00000000 0010 0000 char a = (unsigned char)-1 <<2; printf("%d\n",a);//-4 printf("%u\n",a);//2^32 -4 (unsigned char)-1 : 1111 1111 a : 1111 1100 %d : a -> int 短的 --> 长的 11111111 111111111 111111111 1111 1100 -4 %u : a -> unsigned int 短的 --> 长的 11111111 111111111 111111111 1111 1100 2^32 -4
练习 :
构造一个整数,他的 第7bit位 和 第 28bit位为 1,其他的bit位 为 0 ,该如何构造?
(1<<7)|(1<<28) (1<<7) + (1<<28)
(6) 按位右移 >>
x >> n 双目运算符, 从左到右结合, "把x按bit为, 整体右移n位"
低位右移后,直接舍弃,高位补什么呢?
对于无符号数(x) , 高位全部补0,
对于有符号数(x),高位全部补符号位.
练习 :
1.分析如下程序的输出结果,假设 int 32bits
int main() { int a = -1; 11111111 111111111 111111111 11111111 a = a >> 31; 11111111 111111111 111111111 11111111 printf("%d\n",a);//-1 printf("%u\n",a);//2^32 -1 return 0; }
int main() { unsigned int a = -1; 11111111 111111111 111111111 11111111 a = a >> 31; 00000000 00000000 00000000 00000001 printf("%d\n",a);//1 printf("%u\n",a);//1 return 0; }
2.分析如下程序的输出结果, 假设int 32bits
int main() { char a = -1; int b = a >> 31; -1 : 11111111 11111111 11111111 11111111 a : 11111111 a -> int : 11111111 11111111 11111111 11111111 b : 11111111 11111111 11111111 11111111 printf("%d\n",b);//-1 printf("%u\n",b);//2^32 -1 return 0; }
int main() { unsigned char a = -1; int b = a >> 31; -1 : 11111111 11111111 11111111 11111111 a : 11111111 a -> int : 00000000 00000000 00000000 11111111 b : 00000000 00000000 00000000 00000000 printf("%d\n",b);//0 printf("%u\n",b);//0 return 0; }
(7) 掩码(mask)
掩码(mask) 是一个位模式,表示从一个字(word,32bits)中选出的位集合.
掩码中为 1 的那些bit位, 就是我们想要选取的bit位的集合.
如 :
如果我要选取的第0个bit 和 第1个bit, 则掩码为 : 0x3 00000000 00000000 000000000 00000011 如果 mask = 0xff,表示我选取低8bits 00000000 00000000 00000000 11111111
例子 :
假设有一个整型变量 x = 0x345678AB,我想选取x的低8位,该如何操作?
低8位的掩码 mask = 0xff
x & mask ==> x & 0xff ==> 0xAB
3.5 赋值运算符
a = b,双目运算符,结合性==从右至左==,"把表达式b的值 赋值给 a"
赋值运算符的优先级 排倒数第二, 倒数第一是逗号运算符.
a = b; 把表达式 b 的值,赋值给a(把这个值写入到 a 的地址中去, a需要具备一个可写的地址,"左值") 一般来说, a 为一个可变的数据对象 "变量" 赋值运算符的 左边的操作数 必须为一个 "可写的地址" 左值 ,变量
如 :
5 = 5;//error 2 + 3 = 5;//error int x = 1.y = 3; x + y = 5;//error x = 5;//可以的 x++ = 6;//error
赋值表达式 : 由赋值运算符连接操作数的式子, 称之为赋值表达式
赋值表达式的值,就是赋完值后左边的那个操作数的值.
如 :
x = 5 这是一个赋值表达式,这个表达式的值就是最后x的值, 5 y = x = 5; 这是一个合法的赋值表达式!! ==> y = (x = 5) 把 表达式 "x = 5" 的值,赋值给y
复合赋值运算符 : 赋值运算符可以和算术运算符,位运算符组合成 复合运算符.
+= -= *= /= %= <<= >>= |= &= ^= a += b ==> a = a + b sum <<=5 ==> sum = sum <<5 sum -=i ==> sum = sum - i
3.6 条件运算符
? : 三目运算符 expressions ? a : b 先判断 expressions 的值, 如果他的值为真(非0),则整个表达式的值为 a 如果他的值为假(0) , 则整个表示式的值为b. 结合性 是从右向左.
如 :
if(a > b) { x = 250; } else { x = 360; } x = ( a > b ? 250 : 360 ); or a > b ? (x = 250) : (x = 360)
3.7 逗号运算符
a , b 双目运算符,结合性从左到右结合, 优先级是最低的.
先算表达式 a 的值, 然后在算表达式 b 的值, 整个逗号表达式的值就是最右边表达式b的值.
表达式 1 , 表达式2
例子 :
a = 3 , 2
这是一个逗号表达式,他的指值为 2
(a = 3) , 2
a = (3 , 2)
这是一个赋值表达式,把 "表达式(3 , 2)"的值,赋值给a
a = 3, a = 2
这个也是一个逗号表达式
a = 3 , a = 2 ,a = 4
这个也是逗号表达式 ==> (a = 3 , a = 2) , a = 4
...
逗号表达式的形式可以是这样的 : 扩展的逗号表达式 表达式1 , 表达式2 , 表达式3, ...,表达式n
求值的顺序: 先求表达式1的值,然后求表达式2的值,...,最后求表达式n的值,
整个逗号表达式的值,就是最右边的那个表达式的值.
3.8 指针运算符
* 指向运算符 & 取地址符 后面在讲
3.9 求字节运算符
sizeof
3.10 分量运算符
. ->
3.11 下标运算符
[] 取数组元素 int a[10]; a[0] a[1] ...
3.12 强制类型转换运算符
(类型)表达式 如 : (int)3.5 (int)(3.1 + 4.7)
3.13 其他
函数调用运算.
4 运算符的优先级和结合性
运算符 结合性 () [] -> . 从左向右 ! ~ ++ -- +(正号) -(负号) *(指针) &(取地址) (type) sizeof 从右向左 * / % 从左到右 + 加 - 减 从左到右 << >> 从左到右 < <= > >= 从左到右 == != 从左到右 & 按位与 从左到右 ^ 按位异或 从左到右 | 按位或 从左到右 && 从左到右 || 从左到右 ? : 从右向左 = += -= *= /= %= &= 从右向左 ^= |= <<= >>= 从右向左 , 从左向右
通用办法 : ==如果不确定优先级,那就用 ( ) 来解决==