光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0

????博客主页:https://blog.csdn.net/WhereIsHeroFrom
????欢迎各位 ????点赞 ⭐收藏 ????评论,如有错误请留言指正,非常感谢!
????本文由 英雄哪里出来 原创,转载请注明出处,首发于 ???? CSDN ????
作者的专栏:
  ????C语言基础专栏《光天化日学C语言》
  ????C语言基础配套试题详解《C语言入门100例》
  ????算法进阶专栏《夜深人静写算法》

文章目录

一、前言

  本文作者是从 2007 年开始学 C语言 的,不久又接触了C++,基本就是 C/C++ 技术栈写了 14 年的样子,不算精通,但也算差强人意。著有《夜深人静写算法》系列,且承诺会持续更新,直到所有算法都学完。主要专攻 高中 OI 、大学 ACM、 职场 LeetCode 的全领域算法。由于文章中采用 C/C++ 的语法,于是就有不少读者朋友反馈语言层面就被劝退了,更何况是算法。
  于是,2021 年 06 月 12 日,《光天化日学C语言》 应运而生。这个系列文章主要服务于高中生、大学生以及职场上想入坑C语言的志同道合之人,希望能给祖国引入更多编程方面的人才,并且让自己的青春不留遗憾!
  这一章的主要内容是取反运算符的应用。
光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0

二、人物简介
  • 第一位登场的就是今后会一直教我们C语言的老师 —— 光天。
    光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0
  • 第二位登场的则是今后会和大家一起学习C语言的没什么资质的小白程序猿 —— 化日。
    光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0
三、取反运算符
  • 取反运算符是一个单目位运算符,也就是只有一个操作数,表示为~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,而在计算机中,两个互为相反数的数字相加等于 2 n 2^n 2n。
  • 换言之,互为相反数的两个数互补,补成 2 n 2^n 2n。
  • 对于 32位整型, n = 32 n = 32 n=32;对于 64位整型, n = 64 n = 64 n=64。所以补码也可以表示成如下形式:
  • [ x ] 补 = { x ( 0 ≤ x < 2 n − 1 ) 2 n + x ( − 2 n − 1 ≤ x < 0 ) [x]_补 = \begin{cases}x & (0 \le x \lt 2^{n-1})\\ 2^{n} + x & (-2^{n-1} \le x \lt 0)\\ \end{cases} [x]补​={x2n+x​(0≤x<2n−1)(−2n−1≤x<0)​
  • 于是,对于int类型,就有:
  • x + ( − x ) = 2 32 x + (-x) = 2^{32} x+(−x)=232
  • 因此, − 2 = 2 32 − 2 -2 = 2^{32} - 2 −2=232−2。
  • 于是,我们开始数数……
2^32        = 1 00000000 00000000 00000000 00000000
2^32 - 1    =   11111111 11111111 11111111 11111111
2^32 - 2    =   11111111 11111111 11111111 11111110
...
四、取反运算符的应用

1、0 的取反

【例题1】0 的取反结果为多少呢?

光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0

  • 首先对源码进行取反,得到:
 ~ 00000000 00000000 00000000 00000000
 --------------------------------------
   11111111 11111111 11111111 11111111
  • 这个问题,我们刚讨论完,这个答案为 2 32 − 1 2^{32}-1 232−1。但是实际输出时,你会发现,它的值是-1
  • 这是为什么?
  • 搞得我一头雾水。
  • 原因是因为在C语言中有两种类型的int,分别为unsigned intsigned int,我们之前讨论的int都是signed int的简称。

1)有符号整型

  • 对于有符号整型signed int而言,最高位表示符号位,所以只有31位能表示数值,能够表示的数值范围是: − 2 31 ≤ x ≤ 2 31 − 1 -2^{31} \le x \le 2^{31}-1 −231≤x≤231−1
  • 所以,对于有符号整型,输出采用%d,如下:
#include <stdio.h>
int main() {
    printf("%d\n", ~0 );
    return 0;
}
  • 结果为:
-1

2)无符号整型

  • 对于无符号整型unsigned int而言,由于不需要符号位,所以总共有32位表示数值,数值范围为:
  • 0 ≤ x ≤ 2 32 − 1 0 \le x \le 2^{32}-1 0≤x≤232−1
  • 对于无符号整型,输出采用%u,如下:
#include <stdio.h>
int main() {
    printf("%u\n", ~0 );
    return 0;
}
  • 结果为:
4294967295
  • 即 2 32 − 1 2^{32}-1 232−1。

2、相反数

【例题2】给定一个int类型的正数 x x x,求 x x x 的相反数(注意:不能用负号)。

  • 这里,我们可以直接利用补码的定义,对于正数 x x x,它的相反数的补码就是 x x x 二进制取反加一。即:~x + 1
#include <stdio.h>
int main() {
    int x = 18;
    printf("%d\n", ~x + 1 );
    return 0;
}
  • 运行结果如下:
-18

3、代替减法

【例题3】给定两个int类型的正数 x x x 和 y y y,实现 x − y x - y x−y(注意:不能用减号)。

  • 这个问题比较简单,如果上面的相反数已经理解了,那么,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 x x 和 y y y,实现 x + y x + y x+y(注意:不能用加号)。

  • 我们可以把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

光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0

通过这一章,我们学会了:
  1)按位取反运算符;
  2)补码的运算;
  3)有符号整型和无符号整型;
  4)相反数、加法、减法、等于判定的另类解法;

  • 希望对你有帮助哦 ~ 祝大家早日成为 C 语言大神!

课后习题

光天化日学C语言(17)- 位运算 ~ 的应用 | 0 变 1、1 变 0


????博客主页:https://blog.csdn.net/WhereIsHeroFrom
????欢迎各位 ????点赞 ⭐收藏 ????评论,如有错误请留言指正,非常感谢!
????本文由 英雄哪里出来 原创,转载请注明出处,首发于 ???? CSDN ????
作者的专栏:
  ????C语言基础专栏《光天化日学C语言》
  ????C语言基础配套试题详解《C语言入门100例》
  ????算法进阶专栏《夜深人静写算法》

上一篇:运算符


下一篇:JVM的艺术-对象创建与内存分配机制深度剖析