目录
前言
一、原码、反码、补码的基础概念
1.原码
2.反码
3.补码
二、原码、反码、补码的计算方法
1.原码
2.反码
3.补码
三、算术操作符
四、移位操作符
1. 左移操作符
移位规则:
2. 右移操作符
移位规则:
(1) 逻辑移位
(2) 算术移位
五、位操作符
1. 按位与运算(AND)
2. 按位或运算(OR)
3. 按位异或运算(XOR)
4. 取反运算(NOT)
5. 位运算的应用
(1)判断整数奇偶
(2)二进制数选取指定位
(3)将指定位设置为1
(4)反转指定位
(5)交换两个数 —— 不借助第三变量
(6)将二进制最右侧为1的二进位改为0
(7)计算二进制中二进位为1的个数
(8)判断某数是否为2的幂次方
六、赋值操作符
七、单目操作符
八、关系操作符
九、逻辑操作符
1.区分逻辑与和按位与
2.区分逻辑或和按位或
十、条件操作符(三目操作符)
使用条件表达式得到两个数的较大值
十一、逗号表达式
十二、下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
2. ( ) 函数调用操作符
3. 访问一个结构的成员
十三、操作符的属性
1. 复杂表达式的求值有三个影响的因素
操作符优先级
表格说明
前言
学了这么长时间的C语言,我也刷了不少的题目,其中每到遇见关于操作题这样的概念类型的选择题我都要标记一下,或者直接瞎选一个。现在趁着刚刚考试完期末周的劲头还没有过去,硕硕就赶紧复习加上查找资料创作出来了一篇关于C语言操作符的博客。各位看官坐稳扶好了,我们要发车了
一、原码、反码、补码的基础概念
在要学习下面的位操作符之前,让我们先了解原码、反码和补码的概念。对于一个数计算机要使用一定的编码方式进行存储,原码、反码、补码是机器存储一个具体数字的编码方式。
1.原码
原码就是符号位加上真值的绝对值,即:用第一位表示符号,其余位表示值。
比如:如果是8位二进制:
[+1] 正一的原码 = 0 000 0001
[-1] 负一的原码 = 1 000 0001
第一位是符号位
因为第一位是符号位,所以8位二进制数的取值范围就是:(即第一位不表示值,只表示正负。)
[1111 1111 , 0111 1111] 即 [-127 , 127]
总结一句话:原码是人脑最容易理解和计算的表示方式。
2.反码
反码的表示方法是:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
值 | 原码 | 反码 |
+1 | 0 000 0001 | 0 000 0001 |
– 1 | 1 000 0001 | 1 111 1110 |
可见如果一个反码表示的是负数,人脑无法直观的看出来它的数值。通常要将其转换成原码再计算。
3.补码
补码的表示方法是:正数的补码就是其本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。(也即在反码的基础上+1)
值 | 原码 | 反码 | 补码 |
+1 | 0 000 0001 | 0 000 0001 | 0 000 0001 |
– 1 | 1 000 0001 | 1 111 1110 | 1 111 1111 |
对于负数,补码表示方式也是人脑无法直观看出其数值的。通常也需要转换成原码再计算其数值。
二、原码、反码、补码的计算方法
1.原码
原码:将最高位作为符号位(0表示正,1表示负),其它数字位代表数值本身的绝对值的数字表示方式。
2.反码
反码:如果是正数,则表示方法和原码一样;如果是负数,符号位不变,其余各位取反,则得到这个数字的反码表示形式。
3.补码
补码:如果是正数,则表示方法和原码一样;如果是负数,则将数字的反码加上1(相当于将原码数值位取反然后在最低位加1)。
三、算术操作符
算数操作符一共有五个
1、+(加)
2、-(减)
3、*(乘)
4、/(除)
5、%(取模)
1. 除了 %(取模) 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / (除)操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % (取模)操作符的两个操作数必须为整数,返回的是整除之后的余数。
四、移位操作符
<< | 左移操作符 |
>> | 右移操作符 |
注:移位操作符的操作数只能是正数。
注:移位操作符的操作数只能是整数。
1. 左移操作符
移位规则:左边抛弃、右边补0
2. 右移操作符
移位规则:
首先右移运算分两种:
⭕逻辑移位
⭕ 算术移位
(1) 逻辑移位
左边用0填充,右边丢弃
(2) 算术移位
左边用原该值的符号位填充,右边丢弃
警告
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10;num>>-1;//error
五、位操作符
✅位操作符有:
& | ^ | //按位与 //按位或 //按位异或 |
注:他们的操作数必须是整数。
1. 按位与运算(AND)
✅按位与运算符为&。其功能是对两个二进制数的每一个二进位进行与运算。
& 按位与: 两个条件同时为真(1)的情况下,运算结果为真,换句话说就是两个条件都是1才为1,否则为0。
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
2. 按位或运算(OR)
✅按位或运算符为|
。其功能对两个二进制数的每一个二进位进行或运算。
| 按位或 :任意一个条件为真(1)的情况下,运算结果为1,就是只要有一个1则为1,否则为0。
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
3. 按位异或运算(XOR)
✅按位异或运算符为^。其功能是对两个二进制数的每一个二进位进行异或运算。
^ 按位异或:两个条件中只有一个条件为真(1)的情况下,运算结果为真。也就是说,相异才为 真,相同则为假。
0 ^ 0 = 0
1 ^ 0 = 1
0 ^ 1 = 1
1 ^ 1 = 0
4. 取反运算(NOT)
✅取反运算符为~
。其功能是对一个二进制数的每一个二进位进行取反运算。
取反运算规则:使数字1变为0,0变为1。
~0 = 1
~1 = 0
5. 位运算的应用
(1)判断整数奇偶
一个整数,只要是偶数,其对应二进制数的末尾一定为0;只要是奇数,其对应二进制数的末尾一定为1。所以,我们通过与1进行按位与运算,即可判断某个数是奇数还是偶数。
(x & 1) == 0
为偶数。(x & 1) == 1
为奇数
(2)二进制数选取指定位
如果我们想要从一个二进制数X中取出某几位,使取出位置上的二进位保留原值,其余位置为0,则可以使用另一个二进制数 Y ,使该二进制数上对应取出位置为1,其余位置为0。然后令两个数进行按位与运算(X & Y
),即可得到想要的数。
(3)将指定位设置为1
如果我们想要把一个二进制数 X中的某几位设置为1,其余位置保留原值,则可以使用另一个二进制数 Y,使得该二进制上对应选取位置为1,其余位置为0。然后令两个数进行按位或运算(X | Y
),即可得到想要的数。
(4)反转指定位
如果我们想要把一个二进制数 X 的某几位进行反转,则可以使用另一个二进制数 Y ,使得该二进制上对应选取位置为1,其余位置为0。然后令两个数进行按位异或运算(X ^ Y
),即可得到想要的数。
(5)交换两个数 —— 不借助第三变量
通过按位异或运算可以实现交换两个数的目的(只能用于交换两个整数)。
#include int main(){int a = 10;int b = 20;a = a^b;b = a^b;a = a^b;printf("a = %d b = %d\n", a, b);return 0;}
(6)将二进制最右侧为1的二进位改为0
如果我们想要将一个二进制数 X 最右侧为1的二进制位改为0,则只需通过X & (X - 1)
的操作即可完成。
(7)计算二进制中二进位为1的个数
从“将二进制最右侧为1的二进位改为0 ”中得知,通过X & (X - 1)
我们可以将二进制 X 最右侧为1的二进制位改为0,那么如果我们不断通过X & (X - 1)
操作,最终将二进制 X变为0,并统计执行次数,则可以得到二进制中二进位为1的个数。
#include int TheOne(int x){int count = 0;while(x){x = x & (x - 1);count++;}return count;}
(8)判断某数是否为2的幂次方
通过判断X & (X - 1) == 0
是否成立,即可判断 X是否为2的幂次方。这是因为:
凡是2的幂次方,其二进制数的某一高位为1,并且仅此高位为1,其余位都为0。
不是2的幂次方,其二进制数存在多个值为1的位。
接下来我们使用X & (X - 1)
操作,将原数对应二进制数最右侧为1的二进位改为0之后,得到新值:
- 如果原数是2的幂次方,则通过
X & (X - 1)
操作之后,新值所有位都为0,值为0。- 如果该数不是2的幂次方,则通过
X & (X - 1)
操作之后,新值仍存在不为0的位,值肯定不为0。
所以我们可以通过是否为0即可判断该数是否为2的幂次方。
六、赋值操作符
赋值操作符是一个很好用的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值
复合赋值符
1、 + =
2、 – =
2、 * =
4、 / =
5、%=
6、 >>=
7、 <<=
8、 &=
9、 |=
10、^=
这些运算符都可以写成复合的效果
int x = 10;x = x+10;x += 10;//复合赋值//其他运算符一样的道理。这样写更加简洁。
七、单目操作符
! | 逻辑反操作 |
– | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
– – | 前置、后置- – |
+ + | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
八、关系操作符
> | 用于比较左右两个值的大小 |
>= | 用于比较左右两个值的大小 |
< | 用于比较左右两个值的大小 |
<= | 用于比较左右两个值的大小 |
! = | 用于测试“不相等” |
== | 用于测试“相等” |
这些关系运算符比较简单,但是我们要注意一些运算符使用时候的陷阱。
警告:在编程的过程中== 和=不小心写错,导致的错误
九、逻辑操作符
&& | 逻辑与 |
|| | 逻辑或 |
1.区分逻辑与和按位与
按位与:1&2—–>0
逻辑与:1&&2—->1
2.区分逻辑或和按位或
按位或:1|2—–>3
逻辑或:1||2—->1
十、条件操作符(三目操作符)
exp1 " />三目操作符 ⭕如果表达式1为真,那么就计算表达式2,表达式2的结果为整个式子的 结果;
⭕如果表达式1为假,那么就计算表达式3,表达式3的结果为整个式子的结果。
例:问:将下列式子转化为条件表达式是什么样的?
if (a > 5)b = 3;else b = -3;
答案:
b = (a > 5 ? 3 : -3);
使用条件表达式得到两个数的较大值
#includeint main(void){ int a = 0, b = 0, c = 0; printf("请输入两个数:\n"); scanf_s("%d %d", &a, &b); c = (a > b ? a : b); printf("较大的数为:%d\n", c); return 0;}
十一、逗号表达式
exp1, exp2, exp3, …expN
⭕逗号表达式,就是用逗号隔开的多个表达式。
⭕逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int a = 1;int b = 2;int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
上面C的值最后等于13
十二、下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
int arr[10];//创建数组arr[9] = 10;//实用下标引用操作符。[ ]的两个操作数是arr和9。
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include void test1(){printf("hehe\n");}void test2(const char *str){printf("%s\n", str);}int main(){test1(); //实用()作为函数调用操作符。test2("hello bit.");//实用()作为函数调用操作符。return 0;}
3. 访问一个结构的成员
. 结构体.成员名 -> 结构体指针->成员名
#include struct Stu{char name[10];int age;char sex[5];double score;}void set_age1(struct Stu stu){stu.age = 18;}void set_age2(struct Stu* pStu){pStu->age = 18;//结构成员访问}int main(){struct Stu stu;struct Stu* pStu = &stu;//结构成员访问stu.age = 20;//结构成员访问set_age1(stu);pStu->age = 20;//结构成员访问set_age2(pStu);return 0;}
十三、操作符的属性
1. 复杂表达式的求值有三个影响的因素
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级(表)
优先级
运算符
名称或含义
使用形式
结合方向
说明
1
[ ]
数组下标
数组名[常量表达式]
左到右
-----
( )
圆括号
(表达式)/函数名(形参表)
-----
.
成员选择(对象)
对象.成员名
-----
->
成员选择(指针)
对象指针->成员名
-----
2
-
负号运算符
-表达式
右到左
单目运算符
(类型)
强制类型转换
(数据类型)表达式
-----
++
前置自增运算符
++变量名
单目运算符
++
后置自增运算符
变量名++
单目运算符
--
前置自减运算符
--变量名
单目运算符
--
后置自减运算符
变量名--
单目运算符[4]
*
取值运算符
*指针变量
单目运算符
&
取地址运算符
&变量名
单目运算符
!
逻辑非运算符
!表达式
单目运算符
~
按位取反运算符
~表达式
单目运算符
sizeof
长度运算符
sizeof(表达式)
-----
3
/
除
表达式/表达式
左到右
双目运算符
*
乘
表达式*表达式
双目运算符
%
余数(取模)
整型表达式/整型表达式
双目运算符
4
+
加
表达式+表达式
左到右
双目运算符
-
减
表达式-表达式
双目运算符
5
<<
左移
变量
左到右
双目运算符
>>
右移
变量>>表达式
双目运算符
6
>
大于
表达式>表达式
左到右
双目运算符
>=
大于等于
表达式>=表达式
双目运算符
<
小于
表达式
双目运算符
<=
小于等于
表达式
双目运算符
7
==
等于
表达式==表达式
左到右
双目运算符
!=
不等于
表达式!= 表达式
双目运算符
8
&
按位与
表达式&表达式
左到右
双目运算符
9
^
按位异或
表达式^表达式
左到右
双目运算符
10
|
按位或
表达式|表达式
左到右
双目运算符
11
&&
逻辑与
表达式&&表达式
左到右
双目运算符
12
||
逻辑或
表达式||表达式
左到右
双目运算符
13
? :
条件运算符
表达式1? 表达式2: 表达式3
右到左
三目运算符
14
=
赋值运算符
变量=表达式
右到左
-----
/ =
除后赋值
变量/=表达式
-----
* =
乘后赋值
变量*=表达式
-----
% =
取模后赋值
变量%=表达式
-----
+ =
加后赋值
变量+=表达式
-----
- =
减后赋值
变量-=表达式
-----
<< =
左移后赋值
变量
-----
>> =
右移后赋值
变量>>=表达式
-----
& =
按位与后赋值
变量&=表达式
-----
^ =
按位异或后赋值
变量^=表达式
-----
| =
按位或后赋值
变量|=表达式
-----
15
,
逗号运算符
表达式,表达式,…
左到右
从左向右顺序运算
表格说明
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符