前言
Hello各位老铁们,还记得我在 初识C语言(下) 中介绍的操作符吗?在初识部分,我向大家罗列了C语言中所有的操作符,并且选择性的讲解了一些常用操作符。本章在此基础之上,继续深入学习和使用操作符!
一、操作符分类
下面是C语言中所有的操作符种类:
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
在初识C语言(下)中,小编已经对部分操作符进行了详细介绍,这里就不在赘述。下面就对余下操作符进行讲解:
二、操作符精讲
🍑1、原码、反码、补码
在介绍移位操作符和位操作符之前,我们有必要了解一下有符号整数在计算机中的存储方式:
- 首先整数在计算机中以2进制表示,其中有符号整形的2进制又包含原码、反码以及补码。
- 规定有符号整数的最高位表示符号位,0表示正数,1表示负数。其它位为数值位。
- 正整数的原码、反码、补码相同。
- 负整数的原码:即直接将数值翻译为2进制位+符号位。
- 负整数的反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
- 负整数的补码:反码+1就得到补码。
- 整数在内存实际存放的是补码,而我们使用的是原码。
📝例如:
正整数的原、反、补相同:
负整数的原反补需要转换:
🍑2、移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
(1)左移操作符
移位规则:左边抛弃、右边补0
📝例如:以4和-4为例
注意:a<<1,当a的未被赋值的情况下,自身的值不会发生变化。右移同理
代码展示:
#include<stdio.h> int main() { int a = 4; //00000000000000000000000000000100 - 4的补码 int b = a << 1;//把a向左移动一位 //00000000000000000000000000001000 - 8的补码 printf("a=%d b=%d\n", a, b); return 0; }
#include<stdio.h> int main() { int a = -4; //10000000000000000000000000000100 - -4的原码 //11111111111111111111111111111011 - -4的反码 //11111111111111111111111111111100 - -4的补码 int b = a << 1;//把a向左移动一位 //11111111111111111111111111111000 - b中存储的补码 //11111111111111111111111111110111 - b的反码 //10000000000000000000000000001000 - b的原码 //-8 printf("a=%d b=%d\n", a, b); return 0; }
拓展:左移是把2进制向高位移动,每移动1位有扩大2倍的效果
(2)右移操作符
移位规则:
- 逻辑移位 左边用0填充,右边丢弃
- 算术移位 左边用原该值的符号位填充,右边丢弃
在C语言中,C语言标准并没有规定有符号数应该使用那种类型的右移,而是取决于编译器。但是绝大多数编译器都采用算术右移。对于无符号数,自然采用逻辑右移。
📝例如:同样以4和-4为例
#include<stdio.h> int main() { int a = 4; //00000000000000000000000000000100 补码 int b = a >> 1; //00000000000000000000000000000010 补码 printf("a=%d b=%d\n", a, b); return 0; }
#include<stdio.h> int main() { int a = -4; //10000000000000000000000000000100 - -4的原码 //11111111111111111111111111111011 - -4的反码 //11111111111111111111111111111100 - -4的补码 int b = a >> 1; //11111111111111111111111111111100 //11111111111111111111111111111110 - b在内存中的补码 //11111111111111111111111111111101 - b的反码 //10000000000000000000000000000010 - b的原码 //-2 printf("a=%d b=%d\n", a, b); return 0; }
拓展:右移是把2进制向低位移动,每移动1位有缩小2倍的效果
🍑3、位操作符
& 按位与
| 按位或
^ 按位异或
~取反运算符
注:他们的操作数必须是整数。
(1) & 按位与操作符
规则: 有0则为0,两个同时为1才为1
(2) | 按位或操作符
规则: 有1则为1,两个同时为0才为0
(3) ^ 按位异或操作符
规则: 相同为0,相异为1
(4) ~ 按位取反操作符
规则: 0为1,1为0
📝一道变态的面试题:
不能创建临时变量(第三个变量),实现两个数的交换。
方法一:
int main() { int a = 3; int b = 5; a = a + b;//将(a+b)的和存到a中 b = a - b;//a的值为(a+b)的和-b,由于a=a+b即b=(a=a-b) a = a - b;//b的值为(a+b)的和-a,由于a=a+b且b=a,即a=(b=a-b) printf("a=%d b=%d\n", a, b); return 0; }
缺陷:两个整数加和可能越界
方法二:
//思路: //a^a=0 //0^a=a #include <stdio.h> int main() { int a = 10; int b = 20; a = a ^ b; b = a ^ b;//b=a ^ b ^ b a = a ^ b;//a=a^b^a printf("a = %d b = %d\n", a, b); return 0; }
这道题在交换变量内容时一反常态,解题思路比较绕,建议大家反复观看,重在理解。
🍑4、sizeof详解
(1) sizeof+变量或类型
sizeof操作符的作用是:返回操作数的类型长度(以字节为单位)
注意:sizeof的操作数是变量名时可以不带括号,如果是类型名必须带括号
int main() { int a = 10; int* p; int arr[10]; //%zu专门打印sizeof的返回值 printf("%zu\n", sizeof(a));//int 4 printf("%zu\n", sizeof a);//int 4 printf("%zu\n", sizeof(int));//int 4 //printf("%zu\n", sizeof int);//err printf("%zu\n", sizeof(p));//int* 4 printf("%zu\n", sizeof(arr));//(int [10])40去掉数组名剩下的就是数组类型 printf("%zu\n", sizeof(arr[10]));//int 4 return 0; }
(2) sizeof和数组
#include <stdio.h> void test1(int arr[]) { printf("%d\n", sizeof(arr)); } void test2(char ch[]) { printf("%d\n", sizeof(ch)); } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//sizeof 数组名,计算的是整个数组的大小//40 printf("%d\n", sizeof(ch)); //10 test1(arr);//数组传参传递的是首元素的地址//8或4(取决于32位还是64位) test2(ch);//数组传参传递的是首元素的地址 //8或4(取决于32位还是64位) return 0; }
(3) sizeof和表达式
注意:sizeof里面的表达式不参与计算
#include<stdio.h> int main() { short s = 10; int a = 2; s = a + 5; //sizeof返回的是类型的大小,而表达式s=a+5返回的是s的类型。即short类型 //sizeof里面的表达式不参与计算 printf("%d\n", sizeof(s = a + 5));//2 printf("%d\n", s);//7 return 0; } //输出:2,7
🍑5、逻辑操作符
&& 逻辑与
|| 逻辑或
! 逻辑非
(1) 操作符&&与||
&&与||使用和理解还是比较简单的,比如判断闰年:
#include<stdio.h> int main() { int year = 2000; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { printf("是闰年"); } return 0; }
补充:&&与||在表达式计算时是会控制求值顺序的。例如下面一道360的笔试题:
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; //i = a++||++b||d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; } //程序输出的结果是什么?
当i=a++&&++b&&d++时,由于a=0即为假,对于&&操作符,一旦有一个表达式为假,后面的表达式就不在计算,判定整体为假。所以只计算a++,求得:a=1,b=2,c=3,d=4
当i=a++||++b||d++时,a=0为假,++b=3为真,所以a++||++b整体为真,对于||操作符,有真则为真,所以表达式d++不在计算,求得:a=1,b=3,c=3,d=4
(2) 逻辑非:
“逻辑非”就是指本来值的反值。
例如:" !0" 这个逻辑表达式的值为1.
" !1" 这个逻辑表达式的值为0.
🍑6、逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
📝例如:
//代码1 int a = 1; int b = 2; int c = (a>b, a=b+10, a, b=a+1);//逗号表达式 //可求得:(a>b)==0,a=12,a=12,b=13,即c=13 //代码2 //用于判断 if (a =b + 1, c=a / 2, d > 0)
逗号表达式的其它用法:
逗号表达式在一定情境下还可以简化代码。
... count_val(a); while (a > 0) { //业务处理 a = get_val(); count_val(a); } //如果使用逗号表达式,改写: while (a = get_val(), count_val(a), a > 0) { //业务处理 } //是不是简洁多了? ...
总结
本章重点介绍了原、反、补码,移位操作符、位操作符、sizeof、逻辑操作符以及逗号表达式。最后希望看完本文能够对您有所帮助,如有疑问欢迎随时交流!铁汁们,我们下期再见!
下期预告:表达式求值——整形提升与算数转换