☀️光天化日学C语言☀️(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0
一、前言
本文作者是从 2007 年开始学 C语言 的,不久又接触了C++,基本就是 C/C++ 技术栈写了 14 年的样子,不算精通,但也算差强人意。著有《夜深人静写算法》系列,且承诺会持续更新,直到所有算法都学完。主要专攻 高中 OI 、大学 ACM、 职场 LeetCode 的全领域算法。由于文章中采用 C/C++ 的语法,于是就有不少读者朋友反馈语言层面就被劝退了,更何况是算法。
于是,2021 年 06 月 12 日,《光天化日学C语言》 应运而生。这个系列文章主要服务于高中生、大学生以及职场上想入坑C语言的志同道合之人,希望能给祖国引入更多编程方面的人才,并且让自己的青春不留遗憾!
这一章的主要内容是取反运算符的应用。
二、人物简介
- 第一位登场的就是今后会一直教我们C语言的老师 —— 光天。
- 第二位登场的则是今后会和大家一起学习C语言的没什么资质的小白程序猿 —— 化日。
三、取反运算符
- 取反运算符是一个单目位运算符,也就是只有一个操作数,表示为
~x
。 - 取反运算会对操作数的每一位按照如下表格进行运算,对于每一位只有 0 或 1 两种情况。
操作数 | 取反结果 |
---|---|
0 | 1 |
1 | 0 |
#include <stdio.h>
int main() {
int a = 0b1;
printf("%d\n", ~a );
return 0;
}
- 这里
~a
代表的是对二进制数 1 进行取反,直观感受应该是 0。 - 但是实际输出的却是:
-2
- 这是为什么呢?
- 那是因为,这是一个 32 位整数,实际的取反操作是这样的:
~ 00000000 00000000 00000000 00000001
--------------------------------------
11111111 11111111 11111111 11111110
- 32位整数的二进制表示,前导零也要参与取反。
- 而对于一个有符号的 32 位整数,我们需要用最高位来代表符号位,即最高位为 0,则代表正数;最高位为 1,则代表负数;
- 这时候我们就需要引入补码的概念了。
1、补码
- 在计算机中,二进制编码是采用补码的形式表示的,补码定义如下:
正数的补码是它本身,符号位为 0;负数的补码为正数数值二进制位取反后加一,符号位为一;
2、补码举例
- 根据补码的定义,
-2
的补码计算,需要经过两步: - 1)对 2 的二进制进行按位取反,如下:
~ 00000000 00000000 00000000 00000010
--------------------------------------
11111111 11111111 11111111 11111101
- 2)然后加上 1,如下:
11111111 11111111 11111111 11111101
+ 00000000 00000000 00000000 00000001
--------------------------------------
11111111 11111111 11111111 11111110
- 结果正好为我们开始提到的
~1
的结果。
3、补码的真实含义
- 补码的真实含义,其实体现在 “补” 这个字上,在数学上,两个互为相反数的数字相加等于 0,而在计算机中,两个互为相反数的数字相加等于 。
- 换言之,互为相反数的两个数互补,补成 。
- 对于 32位整型, ;对于 64位整型, 。所以补码也可以表示成如下形式:
-
- 于是,对于
int
类型,就有: -
- 因此, 。
- 于是,我们开始数数……
2^32 = 1 00000000 00000000 00000000 00000000
2^32 - 1 = 11111111 11111111 11111111 11111111
2^32 - 2 = 11111111 11111111 11111111 11111110
...
- 近一步了解了
-2
的二进制表示。 - 关于补码的深入内容,详细可以参考这篇文章:《C/C++ 面试 100 例》(九)补码全网最全总结。
四、取反运算符的应用
1、0 的取反
【例题1】0 的取反结果为多少呢?
- 首先对源码进行取反,得到:
~ 00000000 00000000 00000000 00000000
--------------------------------------
11111111 11111111 11111111 11111111
- 这个问题,我们刚讨论完,这个答案为
。但是实际输出时,你会发现,它的值是
-1
。 - 这是为什么?
- 搞得我一头雾水。
- 原因是因为在C语言中有两种类型的
int
,分别为unsigned int
和signed int
,我们之前讨论的int
都是signed int
的简称。
1)有符号整型
- 对于有符号整型
signed int
而言,最高位表示符号位,所以只有31位能表示数值,能够表示的数值范围是:$$-2^{31} \le x \le 2^{31}-1$$ - 所以,对于有符号整型,输出采用
%d
,如下:
#include <stdio.h>
int main() {
printf("%d\n", ~0 );
return 0;
}
- 结果为:
-1
2)无符号整型
- 对于无符号整型
unsigned int
而言,由于不需要符号位,所以总共有32位表示数值,数值范围为: -
- 对于无符号整型,输出采用
%u
,如下:
#include <stdio.h>
int main() {
printf("%u\n", ~0 );
return 0;
}
- 结果为:
4294967295
- 即 。
2、相反数
【例题2】给定一个
int
类型的正数 ,求 的相反数(注意:不能用负号)。
- 这里,我们可以直接利用补码的定义,对于正数
,它的相反数的补码就是
二进制取反加一。即:
~x + 1
。
#include <stdio.h>
int main() {
int x = 18;
printf("%d\n", ~x + 1 );
return 0;
}
- 运行结果如下:
-18
3、代替减法
【例题3】给定两个
int
类型的正数 和 ,实现 (注意:不能用减号)。
- 这个问题比较简单,如果上面的相反数已经理解了,那么,
x - y
其实就可以表示成x + (-y)
,而-y
又可以表示成~y + 1
,所以减法x - y
就可以用x + ~y + 1
来代替。 - 代码实现如下:
#include <stdio.h>
int main() {
int a = 8;
int b = 17;
printf("%d\n", a + ~b + 1 );
return 0;
}
- 运行结果为:
-9
4、代替加法
【例题4】给定两个
int
类型的正数 和 ,实现 (注意:不能用加号)。
- 我们可以把
x + y
变成x - (-y)
,而-y
又可以替换成~y + 1
; - 所以
x + y
就变成了x - ~y - 1
,不用加号实现了加法运算。
#include <stdio.h>
int main() {
int x = 18;
int y = 7;
printf("%d\n", x - ~y - 1 );
return 0;
}
- 运行结果为:
25
通过这一章,我们学会了:
1)按位取反运算符;
2)补码的运算;
3)有符号整型和无符号整型;
4)相反数、加法、减法、等于判定的另类解法;
- 希望对你有帮助哦 ~ 祝大家早日成为 C 语言大神!
课后习题
📢博客主页:https://blog.csdn.net/WhereIsHeroFrom
📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!
📢本文由 英雄哪里出来 原创,转载请注明出处,首发于 🙉 CSDN 🙉
作者的专栏:
👉C语言基础专栏《光天化日学C语言》
👉C语言基础配套试题详解《C语言入门100例》
👉算法进阶专栏《夜深人静写算法》
- 点赞
- 收藏
- 关注作者
评论(0)