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

举报
英雄哪里出来 发表于 2021/07/16 07:36:15 2021/07/16
【摘要】 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,而在计算机中,两个互为相反数的数字相加等于 2 n 2^n
  • 换言之,互为相反数的两个数互补,补成 2 n 2^n
  • 对于 32位整型, n = 32 n = 32 ;对于 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}

  • 于是,对于int类型,就有:
  • x + ( x ) = 2 32 x + (-x) = 2^{32}

  • 因此, 2 = 2 32 2 -2 = 2^{32} - 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 的取反结果为多少呢?

  • 首先对源码进行取反,得到:
 ~ 00000000 00000000 00000000 00000000
 --------------------------------------
   11111111 11111111 11111111 11111111
  • 这个问题,我们刚讨论完,这个答案为 2 32 1 2^{32}-1 。但是实际输出时,你会发现,它的值是-1
  • 这是为什么?
  • 搞得我一头雾水。
  • 原因是因为在C语言中有两种类型的int,分别为unsigned intsigned 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位表示数值,数值范围为:
  • 0 x 2 32 1 0 \le x \le 2^{32}-1

  • 对于无符号整型,输出采用%u,如下:
#include <stdio.h>
int main() {
    printf("%u\n", ~0 );
    return 0;
}
  • 结果为:
4294967295
  • 2 32 1 2^{32}-1

2、相反数

【例题2】给定一个int类型的正数 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 y 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 y 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

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

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

课后习题


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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。