1.表达式
什么是表达式?
表达式就是能够表达某种意思的式子
a+b //把a和b的值 相加
在C语言中 表达式一般是指 运算符连接操作数的式子
a+b 2+3 5 100 ... a=b+c (单独的一个数也可以看做成一个表达式)
注意: 任何的表达式都是有一个确定的值的
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
往高精度转换(在64bits操作系统下)
char --> short --> int --> unsigned int --> long ---> unsigned long
--> long long (8个字节) --> float --> double --> long double (16个字节)
(2) % 取余
在C语言中, %的两个操作数必须都是 整数
5%3 ==> 2
-5%3 ==> -2 // -5 ÷ 3 = -1 ..... -2
5%-3 ==> 2
-5%-3 ==> -2
结论: 余数的符号 和 被除数的符号 一致
(3) 自增 ++ , 让这个操作数的值加1
自减 -- , 让这个操作数的值减1
注意:
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++; ---> {
a = i;
i = i + 1;
}
b = ++i; ---> {
i = i+1;
b = i;
}
练习:
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(关系成立)
1+2 > 0 --> 1(关系成立)
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
1 || a --> 1 只要有一个为1.整个表达式就为1
0与上任何数都为0, 1或上任何数都为1
练习:
int a=1, b=2, c=3, d=4;
int m=1, n=1;
(m = a>b) && (n = c>d);
printf("%d %d \n", m, n); // 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 )
{
printf("Yes\n");
}
4)位运算符
位运算符是按bit位进行运算的
注意: 位运算符 只能作用于整数
~ 按位取反
& 按位与
| 按位或
^ 按位异或
<< 按位左移
>> 按位右移
只有 按位取反~ 是单目运算符, 其余的都是 双目运算符, 结合性:从左至右
运算步骤: 将整数分解成补码(二进制序列), 然后再去按bit位进行运算,没有借位和进位
例子:
(1)按位取反 ~
0 --> 1
1 --> 0
int a = ~3;
printf("%d\n", a );
printf("%u\n", a );
3 00000000 00000000 00000000 00000011
~ 11111111 11111111 11111111 11111100 a在内存中的存放形式
%d : 补码还原
a 11111111 11111111 11111111 11111100
-1 11111111 11111111 11111111 11111011
~ 00000000 00000000 00000000 00000100 绝对值的原码 4
--> -4
%u : 2^32 - 4
int a = ~(-3);
printf("%d\n", a );
printf("%u\n", a );
|-3| 00000000 00000000 00000000 00000011
~ 11111111 11111111 11111111 11111100
+1 -----------------------------------------
11111111 11111111 11111111 11111101 -3的补码
~ 00000000 00000000 00000000 00000010 a在内存中的存放形式
%d : 符号位为0, 为正数, 直接输出 --> 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 ); // 1
printf("%u\n", a ); // 1
3 00000000 00000000 00000000 00000011
5 00000000 00000000 00000000 00000101
& -------------------------------------------
00000000 00000000 00000000 00000001 ---> 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 ); // 7
3 00000000 00000000 00000000 00000011
5 00000000 00000000 00000000 00000101
| ------------------------------------------
00000000 00000000 00000000 00000111 ---> 7
结论:
任何一个bit位与0进行 按位或|运算时, 其结果保留原值
x | 0 ==> x
任何一个bit位与1进行 按位或|运算时, 其结果都为 1
x | 1 ==> 1
(4)按位异或 ^
异或: 求异 "相同为0,不同为1"
a b a^b
1 1 0
1 0 1
0 1 1
0 0 0
例子:
int a = 3 ^ 5;
printf("%d\n", a ); // 6
3 00000000 00000000 00000000 00000011
5 00000000 00000000 00000000 00000101
^ ------------------------------------------
00000000 00000000 00000000 00000110 --> 6
结论:
任何一个bit位与0进行 按位异或^运算时,其结果保留原值
x ^ 0 ==> x
任何一个bit位与1进行 按位异或^运算时,其结果取反
x ^ 1 ==> ~x
练习:
1)交换两个整数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;
==================
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;
a 0000 0011 3
b 0000 0101 5
^ 0000 0110 6 ---> a = a ^ b;
方法三:
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
<<1 0000 101 0 --> 0000 1010 --> 10 --> 5 * 2^1
<<2 00 0101 00 --> 0001 0100 --> 20 --> 5 * 2^2
<<3 0 0101 000 --> 0010 1000 --> 40 --> 5 * 2^3
<<4 0101 0000 --> 80 --> 5 * 2^4
结论:
如果按位左移舍弃的高位全是0,那么左移n位之后,其值就变为 原值 乘以 2的n次幂
练习:
假设有一个整型变量a,要把a的第5bit位 变为0, 其他bit位保持不变
a xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
& 11111111 11111111 11111111 11011111
~ 00000000 00000000 00000000 00100000
1<<5
--> xxxxxxxx xxxxxxxx xxxxxxxx xx0xxxxx
==> a = a & ~(1<<5);
(6)按位右移 >>
按bit位整体向右移
a >> n
规则:
把a转换成二进制bit序列(补码), 再按bit位整体向右偏移n位
低位直接舍弃n位,高位补什么呢?
如果无符号的概念,高位直接补n个0
如果有符号的概念,高位补n个符号位
例子:
(1)
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在内存中的存放形式a是有符号的int,且符号位为1
>>31 11111111 11111111 11111111 11111111%d : 补码还原 --> -1
%u : 2^32 - 1
(2)
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在内存中的存放形式a是 unsigned int
>>31 00000000 00000000 00000000 00000001%d : 1
%u : 1
5)赋值运算符 =
双目运算符
结合性: 从右至左
a = x
把x的值求出来,再把这个值 赋值给a
赋值运算符要求左边的操作数必须是一个可写的地址 (左值lvalue)
5 = 3; //error
2+3 = 5; //error
i++ = 5; //error
i = 5; // OK
赋值表达式:
由赋值运算符连接操作数的表达式
赋值表达式的值就是最后赋值给左边变量的那个值
a = b = 6;
--> a = ( b = 6 );
注意:
赋值时 涉及到类型转换,赋值运算符右边的值 需要转换成 左边变量的类型
左边变量的类型 即 赋值表达式的结果的类型
float a = 1;
复合赋值运算符
赋值运算符 可以和算术运算符,位运算符 组成 复合赋值运算符
+= -= *= /= %= >>= <<= &= |= ^= ~=
例子:
i += 6; ==> i = i + 6;
6)条件运算符 ?:
?: 三目运算符, 结合性: 从右至左
语法:
表达式1 ? 表达式2 : 表达式3
求值顺序:
先计算表达式1的值,
如果表达式1的值为真(非0), 则整个表达式的值 就为 表达式2 的值
如果表达式1的值为假(0), 则整个表达式的值 就为 表达式3 的值
例子:
int a = 5>4 ? 2 : 0; // a --> 2
7)逗号运算符 ,
双目运算符, 结合性: 从左至右
语法:
表达式1, 表达式2
求值顺序:
先求表达式1的值,再求表达式2的值,整个逗号表达式的值为 表达式2的值;
int a=5;
int b=6;
a = (a=7,b=8);
printf("a = %d\n", a ); // 8
逗号表达式的扩展形式:
表达式1, 表达式2, 表达式3, .... 表达式n
求值顺序:
先求表达式1的值, 再求表达式2的值, ... 最后求表达式n的值
整个逗号表达式的值为 表达式n的值
8)求字节运算符 sizeof()
单目运算符
用来求一个对象或者类型所占的内存空间的字节数
例子:
sizeof(char) --> 1
sizeof(int) --> 4
sizeof(5) --> 4
sizeof(1.0) --> 8 (double)
sizeof(1.0f) --> 4 (float)
short s;
sizeof(s+1) --> 4 (int)
=======================
求数据类型 typeof()如果有一个数据x, 这个数据x的类型 typeof(x) --> x的数据类型
例子:
int a = 0; //定义了一个整型变量a定义一个和变量a 类型一样的变量
typeof(a) b; //typeof 适用于 对未知类型变量的定义
typeof(a) --> 求对象a的数据类型
--> inttypeof(3) --> int
typeof(3.0) --> double
9)指针运算符
& 取地址符 (单目运算符)
* 指向符(解引用) (单目运算符)
10)分量运算符 (成员选择运算符)
.
->
引用结构体的成员变量
结构体变量名.成员名
结构体指针->成员名
11)下标运算符 []
引用数组的元素
int a[10]; //数组a, 有10个int类型的元素
a[0], a[1], ... a[9]
12)强制类型转换运算符 ()
语法:
(数据类型)对象
例子:
float a = 3.6;
int b = (int)a; // 3
3.优先级
我们在运算一个表达式的时候,先看优先级,再看是否有惰性运算,再看结合性
运算符 结合性 几目运算符
() [] -> . 从左至右
! ~ ++ -- 从右至左 单目运算符
* / % 从左至右 算术运算符
+ -
< <= > >= 从左至右 关系运算符
== !=
& ^ | << >> 从左至右 位运算符
&& 从左至右 逻辑运算符
||
?: 从右至左 条件运算符(三目运算符)
= += -= *= /= 从右至左 赋值运算符
, 从左至右 逗号运算符
(1)取整型变量x中 第p个bit开始的n个bit
假设p=3, n=4
xxxxxxxx xxxxxxxx xxxxxxxx x dcba xxx
>>p xxxxxxxx xxxxxxxx xxxxxxxx xxxxdcba
& 00000000 00000000 00000000 00001111
1 00000000 00000000 00000000 00000001
<<n 00000000 00000000 00000000 00010000 // 2^n --> 1<<n
-1 00000000 00000000 00000000 00001111
x = ((1<<n)-1) & (x>>p) ;
(2)将整型变量x中 第p个bit开始的n个bit 取反,其他bit位保持不变
x ^ 0 = x
x ^ 1 = ~x
xxxxxxxx xxxxxxxx xxxxxxxx x dcba xxx
^ 00000000 00000000 00000000 0 1111 000
1 00000000 00000000 00000000 00000001
<<n 00000000 00000000 00000000 00010000 // 2^n --> 1<<n
-1 00000000 00000000 00000000 00001111
<<p 00000000 00000000 00000000 01111000
--> xxxxxxxx xxxxxxxx xxxxxxxx x ~d~c~b~a xxx
x = x ^ ( ((1<<n)-1)<<p );