运算符与表达式
1.运算符
什么运算符?
用来进行某种运算的符号
如:
+ - * / %
几目运算符?该运算符需要带几个操作数
单目运算符:该运算符只需要带一个操作数。如:++ --
双目运算符:该运算符需要带两个操作数。 如:+ - * /
三目运算符: 该运算符需要带三个操作数。 如:?:
结合性:决定先算谁后算谁的问题
从左到右结合,从右到左结合
如:
+ 结合性:从左到右结合
a+b
先算表达式a的值,再算表达式b的值
最后a+b
so,在C语言中
a+b 和 b+a的含义是不一样的
例子:
i=5,j=6;
(i++)+(i+j) //5+12 17
(i+j)+(i++) //11+5 16
优先级: 在一个含有多个运算符的表达式中,先算哪个运算符 后算
哪个运算符的问题
单目运算符>算术运算符>关系运算符>逻辑运算符>
条件运算符>赋值运算符>逗号运算符
(1)算术运算符:进行算术运算的运算符
++ --:单目运算符
* / % + -:双目运算符 结合性从左到右。
例子:
3+5 =>8
5/4 =>1(整数进行算术运算结果为整数)
5.0/4 (5*1.0/4) ==>1.25
typeof(1.0)==>double
(double)(3/2) ==> 1.0
(double)3/2 ===> 1.5
用C语言的表达式来描述数学表达式
a/b ==> 1.0*a/b
%:取余,取模。要求两个操作数都必须为整数
5%1.0 ==> error
5%4 =>1
4%5 =>4
结合性的问题:
a+b 和b+a在C语言中的含义是不一样的!!!!
++:自增运算符
i++
++i
--: 自减运算符
i--
--i
++ --单目运算符,要求操作数必须为一个左值 lvalue
左值:可写的地址
变量具备左值的。
5++ error
(a+b)++ error
(a++)++ error
表达式的值 做完表达式后i的值
i++ i i=i+1
++i i+1 i=i+1
i-- i i=i-1
--i i-1 i=i-1
例子:
a.i=5;
//a=i++;//把表达式(i++)的值,赋值给a
//i++这个表达式的值i 5
//做完表达式后i的值 i=i+1 6
a=++i;//把表达式(++i)的值,赋值给a
//++i这个表达式的值i+1 6
//做完表达式后i的值 i=i+1 6
b.
i=6;
(i++)+(i++)+(i++)
拒绝做这种题目!!!
因为这种题目没有任何实际意义!!!
哪个公司的程序员写这种代码,应该早就被辞退了。
(2)关系运算符:
用来判断两个东东关系的运算符
“关系”:数值大小的关系
< <= > >=
== !=
双目运算符,结合性从左到右结合
关系表达式:用关系运算符连接的式子
关系表达式的值:
“关系成立” 1
“关系不成立” 0
如:
5>4 1
3<=2 0
5>4>3
这是一个合法的关系表达式
<==> (5>4)>3
拿表达式5>4的值去和3进行PK
==> 5 >4 1
1 >3 0
在C语言中,5>4>3这个表达式和数学上 5>4>3
的含义是不一样的
在C语言中,5>4>3拿表达式5>4的值去和3进行比较
数学上的5>4>3的含义视?
5>4 并且4>3
“并且” 如何描述呢?
(3)逻辑运算符 :描述逻辑关系的运算符
! 逻辑非 单目运算符 “取反”
&& 逻辑与 双目运算符 “并且” 结合性从左到右
|| 逻辑或 双目运算符 “或者” 结合性从左到右
逻辑表达式:用逻辑运算符连接起来的式子
逻辑表达式的值:
逻辑真 非0
逻辑假 0
例子:
a = 4,b=5
a&&b==>1
a&&0==>0
(3+5)||(0) ==> 1
!a || b ==>1
4&&0||2 ==>1
5>3 && 8<4
1&&0 ==> 0
练习:分析如下程序的输出结果
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);//1 2 3 4 0 1
C语言运算符是“惰性运算”
a.a&&b&&c
只有a为真,才会去判断b的值
只有a和b都为真的时候,才会去判断c的值
b.a||b||c
只要a为真,就不必判断b和c的值
只有a为假,才会去判断b得到值
只有a和b都为假,才会去判断c的值
一句话。如果事先已经知道表达式的值,那么后面
的运算符(或表达式)就不需要执行。
这就是C语言运算符的惰性。
练习:
用逻辑表达式来判断y(年份)是否为闰年:
1)能被4整除,但是不能被100整除
2)能被400整除
能满足上述条件之一,就是闰年。
怎么判断一个数能不能被整除
就是看它的余数 如果余数为0 就能被整除
a%4 == 0 a能够被4整除
1)
0 == y%4 && y%100
2) y%400==0
==>(y%4==0 && y%100)||(y%400==0)
如果要判断不是闰年 用逻辑表达式来表示:
!((y%4==0 && y%100)||(y%400==0))
(4)位运算符
位运算符是按bit位来进行的运算。
位运算符要求操作数是整数。
有如下:
& 按位与
| 按位或
^ 按位异或
~ 按位取反
<<按位左移
>>按位右移
除了~是单目运算符之外,其他位运算符都是双目运算符,结合性都是从左到右
位运算符操作位只能是整数(整型,字符型)
所有的位运算都是需要把操作数变成bit序列,然后再按bit位来运算。
~ 按位取反 :单目运算符
0 ->1
1 ->0
int a=~3;
printf("%d\n",a);//-4
printf("%u\n",a);//2^32-4
3:
00000000 00000000 00000000 00000011
~3:
11111111 11111111 11111111 11111100
%d
11111111 11111111 11111111 11111100
逆运算
11111111 11111111 11111111 11111011
00000000 00000000 00000000 00000100
int a=~(-3);
printf("%d\n",a);//2
printf("%u\n",a);//2
-3:
11111111 11111111 11111111 11111101
~(-3):
00000000 00000000 00000000 00000010
& 按位与:双目运算符 “与”
a b a&b
0 0 0
1 0 0
0 1 0
1 1 1
&如果两个Bit位操作数都为1,结果才为1,否则为0
int a = 3&5;
0000 0011
0000 0101
0000 0001
练习:
假设有一个整型变量a,要把a的第6位变为0,其他位不变
该如何操作,请写出表达式
a&(~(1<<6)) 0000 0001 << 6 0100 0000 64 ~64
a&(~64)
结论:
一个bit位与0进行“按位与 &”操作 结果为0
x&0 == 0
一个bit位与1进行“按位与 &”操作 保留原值
x&1 == x
(-3)&(-5) =? //-7
-3:11111111 11111111 11111111 11111101
-5:11111111 11111111 11111111 11111011
11111111 11111111 11111111 11111001
11111111 11111111 11111111 11111000
00000000 00000000 00000000 00000111
| 按位或:双目运算符 结合性从左到右
a b a|b
0 0 0
1 0 1
0 1 1
1 1 1
按位或,只要有一个Bit操作数为1,其结果就是1
3 | 5 == 7
011
101
111
练习:
有一个整型变量a,要使a的第5bit置1,保持其他位不变,
该如何操作,请写出表达式
a|32 a|(1<<5)
0010 0000
结论:
一个bit位与0进行“按位或 |”操作 保留原值
x | 0 == x
一个bit位与1进行“按位或 |”操作 结果为1
x | 1 == 1
^ (按位异或):双目运算符 ,结合性从左到右
"异或"求异 不同为1,相同为0
a b a^b
0 0 0
1 1 0
1 0 1
0 1 1
2 ^ 32 = ? 34
0000 0010
0010 0000
0010 0010
练习:
有一个整型变量a,要使a的第5bit保留,保持其他位取反,
该如何操作,请写出表达式
1101 1111 ==> ~32 a=a^~32; a=a^(~(1<<5));
结论:
一个bit位与0进行“按位异或 ^"操作 保留原值
x ^ 0 == x
一个bit位与1进行“按位异或 ^”操作 结果取反
x ^ 1 == ~x
练习:
交换两个整数a和b的值,不用临时变量
a = a+b
b = a-b //a+b-b a
a = a-b //a+b-a b
a=a^b;
b=b^a;
a=a^b;
如何证明?
位运算只和当前位相关,因为它既然没有进位,也没有借位。
值需要证明a b 是一个bit位的情况
a b a=a^b b=b^a a=a^b a b
1 1 0 1 1 1 1
1 0 1 1 0 0 1
0 1 1 0 1 1 0
0 0 0 0 0 0 0
<< 按位左移:双目运算符,按bit位整体往左移
a<<n 把a按bit位整体往左移n位
高位左移后,丢弃
低位会空出n位,直接补0
例子:
1<<5
0000 0000 0000 0000 0000 0000 0000 0001 <<5
0000 0000 0000 0000 0000 0000 0010 0000
如果左移后丢弃的高位都是0,那么左移n位,就表示在原值乘以2的n次方
>> 按位右移:双目运算符,按bit位整体往右移
a>>n 把a按bit位整体往右移n位
低位右移后,舍弃。
高位会空出n位,补什么呢?
对于无符号数,高位补0
对于有符号位,高位补符号位
“逻辑移位”
无符号位的概念,逻辑左移也好,逻辑右移也罢
都是补0
“算术移位”
有符号位的概念,算术右移后,高位补符号位
练习:
a.分析如下程序的输出结果
int a = -1;
a = a>>31;
printf("%d\n",a);//-1
printf("%u\n",a);//2^32-1
a:11111111 11111111 11111111 11111111
a>>31
11111111 11111111 11111111 11111111
-----------------
unsigned int a = -1;
a = a>>31;
printf("%d\n",a);//1
printf("%u\n",a);//1
a:11111111 11111111 11111111 11111111
a>>31
00000000 00000000 00000000 00000001
-----------------
b.
char a = -1;
int b = a>>31;
printf("%d\n",b);//-1
printf("%u\n",b);//2^32-1
//a:8bits
1111 1111
a>>31
把a>>31
1111 1111
a->b:短->长 短有符号数 高位补符号位
11111111 11111111 11111111 11111111
a->int:短->长
11111111 11111111 11111111 11111111 >>31
a>>31
11111111 11111111 11111111 11111111
------------------
unsigned char a = -1u;
int b = a>>31;
printf("%d\n",b);//0
printf("%u\n",b);//0
//a:8bits
1111 1111
a->int 短->长
00000000 00000000 00000000 11111111
a>>31 a无符号 高位全部补0
00000000 00000000 00000000 00000000
5)赋值运算符:双目运算符,结合性从右到左。
优先级倒数第二,仅比逗号高。
=
a=x;
把表达式x的值,赋值给a
赋值运算符要求左边的操作数必须为一个
可写的地址(左值)lvalue
5=5;error
2+3=5;error
i++ = 6;error
赋值表达式:由赋值运算符连接操作数的式子
赋值表达式的值就是最后赋值给左边变量的那个值。
b=a=c=6;
==>把表达式c=6赋值给a
==>在把a的值赋值给b
复合赋值运算符:
赋值运算符可以和算术运算符 位运算符组成
复合赋值运算符
+= -= %= *= /=
<<= >>= |= &= ^=
例子:
a += 5;
<==> a=a+5;
a += 5+6;
<==> a=a+(5+6);
abc += 6;
abc = abc+6;
(6)条件运算符
?:三目运算符 结合性是从右到左的
expression?a:b
上面是一个条件表达式
如果表达式expression的值为真,则整个表达式的值为a
如果表达式expression的值为假,则整个表达式的值为b
a = 5>4?1:0;
"惰性”
(7)逗号运算符:双目运算符,优先级是最低的,结合性从左到右
表达式1,表达式2
逗号表达式的值求值顺序:先求表达式1的值,然后再求表达式2的值
整个逗号表达式的值表达式2的值
例子:
int a=5,b=6;
a=(a=6,a+b);
a=6,a=7,a=9.....
逗号表达式扩展形式:
表达式1,表达式2,表达式3,....表达式n
求值顺序是,先求表达式1的值,然后再求表达式2的值,然后
.....最后求表达式n的值
整个逗号表达式的值表达式n的值
(8)指针运算符
*
&
(9)求字节运算符 sizeof
单目运算符,求一个对象或类型所占的空间的字节数
sizeof(x)
sizeof 不要求x存在,但是要求x的类型是确定的
因为只要你的类型是确定的,那么我就可以知道你所占内存大小。
sizeof(int) == 4
int a[10];
sizeof(a[100])== 4
sizeof(1) == 4
==>typeof(x)
==>sizeof(typeof(x))
sizeof(1) == 4
==>typeof(1) ==> int
==>sizeof(typeof(x)) ==> sizeof(int) ==> 4
sizeof(1.0) == 8
int a;
sizeof(a+1) == 4
sizeof(a+1.0) == 8
(10)分量运算符
求结构体的成员变量(分量)
.
->
(11)下标运算符
[]
int a[10];
a[0] a[1]
(12)强制转换运算符
(类型)
如:
(unsigned char)255
(13)其他
表达式
什么是表达式呢?
表达式就是表达某个意思的式子
在C语言中,表达式一般是指用运算符连接起来的式子。
是表达式就一定会有一个值,这个值就是上面表达式表达的“某个意思”
如:
I LOVE YOU
NB
SB
2B
250
作业:
1.取整型变量x中第p位开始n个bit位
如: 0011 xxxx 0110 1111 取出 xxxx ==> 0000 0000 0000 xxxx
2.将x中第p位开始的n个bit位设置为y(整数变量)中的
最右边的n位的值,x的其余各位保持不变
3.将x中的第p位开始的n个bit位取反,其余位保持不变
4.实现循环移位
1)将x循环右移n位
(x<<(sizeof(x)*8-n))|((unsigned)x>>n)
2)将x循环左移n位
(x<<n)|((unsigned)x>>(sizeof(x)*8-n))
5.分析如下程序的输出结果
char c=-56>>30;
printf("%d\n",c);//-1
printf("%u\n",c);//2^32-1
-----------------
char c=-56u>>30;
printf("%d\n",c);//3
printf("%u\n",c);//3
-56
11111111 11111111 11111111 11001000 <--- -56在计算机中存放的形式
带u 就是把上面的当作是unsigned 处理
typeof(-56) ->int
typeof(-56u) -> unsigned int
-56u >> 30
00000000 00000000 00000000 00000011
--->char
00000011
%d:短->长 c 有符号
00000000 00000000 00000000 00000011 ->3 1+2^1 = 3
%u 短->长 c 有符号
00000000 00000000 00000000 00000011 ->3