1、表达式
什么是表达式?
表达式就是能够表达某种意思的式子
a = b + c; //把b和c相加,再把结果赋值a
在C语言中,表达式一般是指 运算符连接操作数的式子
a+b 5 2+2 1000 ....
注意: 任何的表达式都有一个确定的值
2、运算符
我们用来进行运算的某种符号
比如: + - * / % ....
几目运算符:
表示该运算符需要连接几个操作数
单目运算符:表示该运算符需要连接1个操作数,比如: ++ , --, ! , ...
双目运算符:表示该运算符需要连接2个操作数,比如: +, -, *, /, ...
三目运算符:表示该运算符需要连接3个操作数,比如: ? : (条件运算符)
结合性:
代表运算时的优先顺序
例子:
+ 结合性:从左至右
a + b
(1+2) + (3+4)
这个表达式是 先算a,再算b
所以,在C语言中,a+b 和 b+a 是两个不同的含义
1)算术运算符
进行算术运算的运算符
单目运算符: ++ , --
双目运算符: + , - , * , / , %
结合性:从左至右
(1) / 取整
整数除以整数 其结果还是整数 ,如果要变成实数,那么在分子乘以1.0即可
5/2 --> 2
1.0*5 / 2 --> 2.5
=======================
隐式类型转换:高精度的数据和低精度的数据进行运算符时,结果会自动转换成高精度的
比如:
int double --> double
int short --> int
char short --> int
char char --> int
无符号 有符号 --》 无符号
例子:
char a = '0';
char b = 1;
printf("%d\n", a+b); // 49
(2) % 取余
在C语言中,%的两个操作数必须为整数
5%3 --> 2
-5%3 --> -2
5%-3 --> 2
-5%-3 --> -2
结论:余数的符号 和 被除数的符号 一致
(3) 自增 ++ ,让这个操作数的值加1
自减 -- ,让这个操作数的值减1
C中++i和i++都不能作为左值
C++中++i可以作为左值,i++不能作为左值
注意:
1.自增和自减 只能作用于 变量
i++; --> i = i + 1;
5++; --> 5 = 5 + 1; // error
(a+b)++; // error
(a++)++; // error
2.
整个表达式的值 执行后i的值
i++ i i+1
++i i+1 i+1
i-- i i-1
--i i-1 i-1
例子:
a = ++i; ==> {
i = i+1;
a = i;
}
b = i++; ==> {
b = i;
i = i+1;
}
练习:
1)
int i = 5;
int a = i++; // a==5, i==6
int b = ++i; // b==7, i==7
int c = i--; // c==7, i==6
int d = --i; // d==5, i==5
2)
int i = 6;
printf("%d %d\n", i++, ++i); // 6 8 ,实际结果为7 8
printf()函数中的计算顺序是从右至左,打印则是从左至右
但是 不同的编译器 会导致结果不同
++/-- 前置的返回是i本身的值, 后置的返回是缓存区的值
printf("%d\n", i++);
printf("%d\n", ++i);
2)关系运算符
用来判断两个表达式的数值大小关系的运算符
双目运算符 , 结合性:从左至右
> >= == <= < !=
关系表达式:
由关系运算符连接操作数的表达式
关系表达式的值只有两种情况: 1(关系成立), 0(关系不成立)
例子:
5>3 --> 1(关系成立)
3>4 --> 0(关系不成立)
5>4>3 --> 0(关系不成立)
5>4>3 --> (5>4) > 3
--> 1 > 3
--> 0
3)逻辑运算符
表达某种数理逻辑的运算符
&& 逻辑与 双目运算符 “并且” 结合性:从左至右
|| 逻辑或 双目运算符 “或者” 结合性:从左至右
! 逻辑非 单目运算符 “取反”
逻辑表达式:
由逻辑运算符连接操作数的表达式
逻辑表达式的值只有两种情况:1(真,非0), 0(假)
任何非0的数都表示真,但是逻辑表达式为真,其值为1
例子:
int a=4, b=5;
a && b ===> 1
a && 0 ===> 0 只要有一个为0,整个表达式就为0
a || b ===> 1
a || 0 ===> 1 只要有一个为1,整个表达式就为1
0与上任何数都为0,1或上任何数都为1
练习:
int 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
惰性运算:
如果事先知道了表达式的值,那么它就不会运算后面的表达式了
比如:
a && b && c
只有当a的值为真(非0),才会去运算b的值
只有当a和b的值都为真(非0),才会去运算c的值
a || b || c
只有当a的值为假(0)时,才会去运算b的值
只有当a和b的值为假(0)时,才会去运算c的值
练习:
请用逻辑表达式来判断某一年是否为闰年
1)能够被4整除但是不能被100整除
2)能够被400整除
int year;
if( (year%4 == 0 && year%100 != 0) || year%400 == 0 )
{
//闰年
}
4)位运算符
位运算符是按bit位来进行运算的
注意:位运算符 只能作用于整数
~ 按位取反
& 按位与
| 按位或
^ 按位异或
<< 按位左移
>> 按位右移
只有按位取反是单目运算符,其余都是双目运算符,结合性:从左至右
运算步骤:将整数分解成bit序列(补码),然后再去按bit位进行运算
例子:
1)按位取反 ~ 0->1 , 1->0
int a = ~3;
printf("%d\n",a);
printf("%u\n",a);
3 00000000 00000000 00000000 0000 0011 (int)
~ 11111111 11111111 11111111 1111 1100 a (int)
%d : 补码还原
11111111 11111111 11111111 1111 1100 a (int)
11111111 11111111 11111111 1111 1011 -1
00000000 00000000 00000000 0000 0100 ~ 绝对值4
==》 -4
%u : 2^32 - 4
int a = ~(-3);
printf("%d\n",a);
printf("%u\n",a);
3 00000000 00000000 00000000 0000 0011
~ 11111111 11111111 11111111 1111 1100
11111111 11111111 11111111 1111 1101 --> -3
~ 00000000 00000000 00000000 0000 0010 a
%d : 2
%u : 2
2)按位与 &
a b a&b
1 1 1
1 0 0
0 1 0
0 0 0
结论:只有两个bit位都为1时,结果才为1,否则就为0 “有0则0,全1为1”
例子:
int a = 3&5;
printf("%d\n",a);
printf("%u\n",a);
3 00000000 00000000 00000000 0000 0011
5 00000000 00000000 00000000 0000 0101
& 00000000 00000000 00000000 0000 0001 --》 1
结论:
任何一个bit位与0进行“按位与&”运算时,结果都为0
x & 0 == 0
任何一个bit位与1进行“按位与&”运算时,结果都为保留原值
x & 1 == x
3)按位或 |
a b a|b
1 1 1
1 0 1
0 1 1
0 0 0
只有当两个bit位都为0时,结果才为0,否则为1 “有1则1,全0才0”
例子:
int a = 3|5;
printf("%d\n",a);
printf("%u\n",a);
3 00000000 00000000 00000000 0000 0011
5 00000000 00000000 00000000 0000 0101
| 00000000 00000000 00000000 0000 0111 --> 7
结论:
任何一个bit位 与0 进行“按位或|”运算时,结果保留原值
x | 0 == x
任何一个bit位 与1 进行“按位或|”运算时,结果为1
x | 1 == 1
4)按位异或 ^
异或:求异
a b a^b
1 1 0
1 0 1
0 1 1
0 0 0
“相同为0,不同为1”
例子:
int a = 3^5;
printf("%d\n",a);
printf("%u\n",a);
3 00000000 00000000 00000000 0000 0011
5 00000000 00000000 00000000 0000 0101
^ 00000000 00000000 00000000 0000 0110 --> 6
结论:
任何一个bit位 与0 进行“按位异或^”运算时,结果保留原值
x ^ 0 == x
任何一个bit位 与1 进行“按位异或^”运算时,其结果取反
x ^ 1 == ~x
练习:
交换两个整数a和b的值,不能使用临时变量
int a = 5;
int b = 6;
方法一 /*
//临时变量法
int temp;
temp = a;
a = b;
b = temp;
*/
方法二
//异或的方法
a = a^b;
b = b^a;
a = a^b;
注意:
位运算只和当前bit位相关,既没有借位,也没有进位
a 0000 0101 5
b 0000 0110 6
^ 0000 0011 3 --> a = a^b;
b 0000 0110 6
a 0000 0011 3
^ 0000 0101 5 --> b = b^a; --> b --> 5
a 0000 0011 3
b 0000 0101 5
^ 0000 0110 6 --> a = a^b; --> a --> 6
方法三:
a = a + b;
b = a - b;
a = a - b;
5)按位左移 <<
按bit位整体向左移
a << n
规则:
把a转换成二进制bit序列(补码),再按bit位整体向左偏移n位
高位直接舍弃n个位,低位补n个0
例子:
char a = 5 << 3;
5 0000 0101
<<2 0001 0100 --> 20 5 * 2^2
<<3 0010 1000 --> 40 5 * 2^3
5 << 4
0101 0000 --> 80 5 * 2^4
5<<5
1010 0000 --> 160 5 * 2^5
5<<6
0100 0000 --> 64 高位舍弃了1,不满足了
结论:
如果按位左移舍弃的高位全是0,那么左移n位后,其值就变为 原值 乘以 2的n次方
练习:
假设有一个整型变量a,要把a的第5bit位变为0,其他bit位保持不变
a xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
& 11111111 11111111 11111111 11011111
~ 00000000 00000000 00000000 00100000
1<<5 00000000 00000000 00000000 00000001 --> 1
xxxxxxxx xxxxxxxx xxxxxxxx xx0xxxxx
a = ~(1<<5) & a ;
6)按位右移 >>
按bit位整体向右移
a >> n
规则: 把a转换成二进制bit序列(补码),再按bit位整体向右偏移n位
低位直接舍弃n位,高位补什么呢?
如果无符号的概念,高位就直接补0
如果有符号的概念,高位就补符号位
例子:
int a = -1;
a = a >> 31;
printf("%d\n",a);
printf("%u\n",a);
1 00000000 00000000 00000000 00000001
11111111 11111111 11111111 11111110 ~
-1 11111111 11111111 11111111 11111111 +1
>>31 11111111 11111111 11111111 11111111
%d : 补码还原 -1
%u : 2^32 - 1
unsigned int a = -1;
a = a >> 31;
printf("%d\n",a);
printf("%u\n",a);
1 00000000 00000000 00000000 00000001
11111111 11111111 11111111 11111110 ~
-1 11111111 11111111 11111111 11111111 +1
>>31 00000000 00000000 00000000 00000001
%d : 1
%u : 1
unsigned char a = -1;
a = a >> 31;
printf("%d\n",a);
printf("%u\n",a);
-1 11111111 11111111 11111111 11111111
a 11111111
>>31 00000000
%d : 0
%u : 0
5)赋值运算符 =
双目运算符 ,结合性:从右至左
a = x
把x的值求出来,再把整个值 赋值给a
赋值运算符要求左边的操作数必须是一个可写的地址(左值)
5 = 3; //error
2+3 = 5; //error
i++ = 5; //error
i = 6; //right
赋值表达式:
由赋值运算符连接操作数的表达式
赋值表达式的值就是最后赋值给左边变量的那个值
a = b = 6;
--> a = (b=6);
注意:
赋值时 涉及到类型转换,赋值运算符右边的值需要转换成左边变量的类型
左边变量的类型 即 赋值表达式的结果的类型
float a = 1;
复合赋值运算符
赋值运算符可以 和算术运算符、位运算符 组成 复合赋值运算符
+= -= *= /= %= >>= <<= &= |= ^= ~=
例子:
i += 6; <==> i = i + 6;
i += 4*5; <==> i = i + (4*5);
6)条件运算符
? : 三目运算符,结合性:从右至左
语法:
表达式1 ? 表达式2 : 表达式3
求值顺序:
先计算表达式1的值,
如果表达式1的值为真(非0),则整个表达式的值 就为 表达式2的值
如果表达式1的值为假(0),则整个表达式的值 就为 表达式3的值
例子:
int a = 5>4 ? 3 : 2;
a --> 3
7)逗号运算符 ,
双目运算符,结合性:从左至右
语法:
表达式1,表达式2
求值顺序:
先求表达式1的值,再求表达式2的值,整个逗号表达式的值为表达式2的值
int a=5;
int b=6;
a = (a=6, a+b);
printf("%d\n",a); //12
逗号表达式的扩展形式:
表达式1,表达式2,表达式3,表达式4, ... 表达式n
求值顺序:
先求表达式1的值,再求表达式2的值,... 最后求表达式n的值,
整个逗号表达式的值为表达式n的值
8)指针运算符
& 取地址符
* 指向符
9)求字节运算符 sizeof()
单目运算符
用来求一个对象或类型所占的内存空间的字节数
例子:
sizeof(int) --> 4
sizeof(1.0) --> 8 (double)
sizeof(1.0f) --> 4 (float)
short s;
sizeof(s+1); --> 4 (int)
=================
求数据类型 typeof()
如果有一个数据x,这个数据的类型 typeof(x) ---- x的数据类型
例子:
int a = 0; //定义一个整型变量
定义一个和变量a 类型一样的变量
typeof(a) b; //typeof 适用于 对未知类型变量的定义
typeof(a) 表示该数据的类型
typeof(a) <==> int
typeof(3) --> int
typeof(3.14) --> double
10) 分量运算符
.
->
引用结构体的成员变量
student.name
studeng.num
stu->score
11)下标运算符 []
引用数组的元素
int a[10];
a[0], a[1], ... a[9]
12)强制类型转换运算符 ()
语法:
(数据类型)对象
例子:
float a = 3.6;
int b = (int)a; // 3
13)其他
3、优先级
我们在运算一个表达式的时候,先看优先级,再看是否有惰性运算,再看结合性
() [] -> . 从左至右
! ~ ++ -- 从右至左 单目运算符
* / % 从左至右 算术运算符
+ -
< <= > >= 从左至右 关系运算符
== !=
& ^ | << >> 从左至右 位运算符
&& 从左至右 逻辑运算符
||
? : 从右至左 条件运算符
= += -= *= /= 从右至左 赋值运算符
, 从左至右 逗号运算符
编程建议:如果实在记不住,请自觉打括号
4、练习
1)取整型变量x中 第p个bit位开始的n个bit
假设 p=3, n=4
xxxxxxxx xxxxxxxx xxxxxxxx xabcdxxx
>>p xxxxxxxx xxxxxxxx xxxxxxxx xxxxabcd
00000000 00000000 00000000 00001111
1 00000000 00000000 00000000 00000001
1<<n00000000 00000000 00000000 00010000 // 2^n --> 1<<n
-1 00000000 00000000 00000000 00001111
00000000 00000000 00000000 0000abcd
x = (x>>p) & ( (1<<n) -1);
2)将整型变量x中 第p个bit位开始的n个bit 取反,其他位保持不变
x^0 = x
x^1 = ~x
xxxxxxxx xxxxxxxx xxxxxxxx xabcdxxx
^ 00000000 00000000 00000000 01111000
<<p 00000000 00000000 00000000 00001111
1 00000000 00000000 00000000 00000001
1<<n00000000 00000000 00000000 00010000 // 2^n --> 1<<n
-1 00000000 00000000 00000000 00001111
x = (((1<<n)-1)<<p) ^ x ;