运算符与表达式
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 温馨提示: 如果说下面这个问题不是C新手最常见的问题,那么它至少也是令人恼火一个错误!!! 绝大部分其他的语言使用 = 操作符来进行比较 相等性 。在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
练习
分析如下程序的输出结果 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",a,b,c,d,m,n); C语言运算符是”惰性运算“ (1)、a&&b&&c 只有当a为真的时候,才会去判断b 同时a和b都为真的时候,才会去判断c (2)a||b||c 只要当a为真的时候,那么就不会再去判断b和c的值了 只有当a 为假的时候,才需要去判断b 的值 当b 为假的时候,才需要去判断c 的值 in a word ,如果你实现就知道表达式的值 了,那么后面的运算符或者表达式就不需要去指向了!!!因为没有这个必要了
练习
(1)、用逻辑表达式来判断y(年份)是否为 闰年 a、可以被4整除,但是不能被100整除 b、可以被4整除,又可以被400整除 (a)||(b) a:(y%4 == 0 && y%100 !=0) b:(y%4 == 0 && y%400 ==0) (y%4 == 0 && y%100 != 0)||(y%4 == 0 && y%400 == 0) (2)、用逻辑表达式来判断y(年份)是否为非闰年 int u = !((a)||(b))
位运算符
位运算符是按bit为展开来进行运算,位运算符要求操作数必须是一个整数(兼容的整数),因为在进行位运算时,需要把操作数按bit展开。”按每一个bit位来进行操作的“ & 按位与 | 按位或 ^ 按位异或 ~ 按位取反 >> 按位右移 << 按位左移 除了 ~ 是单目运算符之外,其他的位运算符全部是双目运算符,结合性是从左到右的,位运算的操作数只能是整型。所有的位运算都需要把操作数变成bit位序列,然后进行操作
按位取反 ~
1 ---> 0 0 ---> 13:0000 0011 ~3:1111 1100
练习
int a = ~(-3); 3: 0000 0011 取反: 1111 1100 +1 --> -3: 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 ====》1NOTE: 注意&(按位与)与&&(逻辑与)的区别
练习
假设有一个整型变量 a , 要把 a 的第5个bit,变为0 其他的bit不变,你该如何操作!! int a; a = a&~(1<<5)结论: 一个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 == 1NOTE: 要区分 | 按位或 和 || 逻辑或的区别
练习: 假设有一个整型变量a, 要把a 的第28个bit,变为1 。其他位不变,你该如何操作!? a = a | (a << 27)
结论: 一个bit位 与 1 进行 ”按位或|“的一个操作,结果为1 X | 1 ==== 1 一个bit位 与 0 进行 ”按位或|“的一个操作,结果保留原来的值 X | 0 ==== X
按位异或 ^
"异或":求异,不同为1 ,相同就为0a ^ 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
练习: 假设有一个整型变量a,要把a的第5个bit保留,其他bit取反,阁下该如何应对? a = a ^~(1<<5)
结论: 一个 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次方
练习:
分析如下程序的输出结果!! (1)、 int a=-1; a = a >> 31; printf("%d\n",a); //-1 printf("%u\n",a); //2^32-1 -1 : 00000000 00000000 00000000 00000001 11111111 11111111 11111111 11111110 11111111 11111111 11111111 11111111 (2)、 unsigned int a=-1; a = a >> 31; printf("%d\n",a); //1 printf("%u\n",a); //1 -1 : 00000000 00000000 00000000 00000001 11111111 11111111 11111111 11111110 11111111 11111111 11111111 11111111 00000000 00000000 00000000 00000001 (3)、 char a = -1; int b = a >> 31; printf("%d\n",b); //-1 printf("%u\n",b); //2^32-1 a : -1 : 1111 1111 a >> 31 char --> int 11111111 11111111 11111111 11111111 >> 31 11111111 11111111 11111111 11111111 (4)、 unsigned char a = -1; int b = a >> 31; printf("%d\n",b); //0 printf("%u\n",b); //0 a : -1 : 1111 1111 a >> 31 char --> int 00000000 00000000 00000000 11111111 >> 31 00000000 00000000 00000000 00000000
赋值运算符 =
双目运算符 , 结合性从右到左 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;温馨提示: 如果说下面这个问题不是C新手最常见的问题,那么它至少也是令人恼火一个错误!!! 绝大部分其他的语言使用 = 操作符来进行比较 相等性 。在C语言中,你必须要用== 来进行比较,单个= 是赋值 这个的陷阱在于: 在测试相等性的时候出现赋值符号也是合法的!! 它不是一个语法错误 if(a==b) if(a=b) no error
条件运算符 ? :
三目运算符 结合性从右到左 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
其他运算符
如: 函数调用符() int func(int a , int b) { //asdhuiasghduiasd; } int main() { func(10,29); }
总结
运算符的优先级和结合性
= 不同于 ==
由Algol派生而来的大多数程序设计语言,例如:Ada,使用:== 作为赋值运算符,但是符号 = 作为比较运算符,而我们的C使用了另外一种表示方法,符号 = 作为赋值,== 作为比较! 一般来说,赋值运算符肯定是比 比较运算符,出现次数更加多,因此字符较少的 = 作为了赋值运算符 该语句的本意应该是检查x是否等于y if(x = y) { ... break; } 实际上将 y赋值给了x
&和| 不同于 && 和 ||
运算符优先级
单目运算符>算术运算符>关系运算符>逻辑运算符>赋值运算符>逗号运算符 我们需要记住两句话: 1、任何一个逻辑运算符的优先级低于任何一个关系运算符 2、移位运算符的优先级要比算术运算符要低,但是要比关系运算符要高