探秘C语言:位运算符的奥秘

news2024/12/25 1:35:18

在这里插入图片描述

本篇博客会讲解C语言中的6个位操作符:按位取反(~)、按位与(&)、按位或(|)、按位异或(^)、左移(<<)、右移(>>)。这6个操作符都是操作整数的二进制位的。在学习这6个位操作符之前,大家需要先掌握“整数在内存中的存储”这个知识点,详见我的上一篇博客:戳这里跳转。

按位取反(~)

按位取反操作符(~)是一个一元操作符,用于对一个整数的每一位进行取反操作。具体来说,它会将每一位的0变为1,1变为0。

举个例子:

int a = 0;
b = ~a;
// 此时a、b分别是多少?

当a存储的是0时,内存中存放的是补码,即32位全是0:00000000000000000000000000000000,按位取反之后就变成全1了,即11111111111111111111111111111111。由于还在内存中放着,就还是补码,转换成原码后就得到10000000000000000000000000000001,即-1。所以此时b就得到了-1,但是按位取反操作符并没有改变a,所以a还是0。

按位与(&)

按位与操作符(&)是一种二元操作符,用于对两个整数进行按位与运算。按位与操作符将两个整数补码的二进制进行按位比较,只有在相应位置上都为1时,结果才为1,否则结果为0。

例如,假设有两个整数a和b,它们在内存中存储的二进制(其实就是补码)分别为:

a = 00000000000000000000000010101100
b = 00000000000000000000000011001010

那么a & b的结果为:

a & b = 00000000000000000000000010001000

举一个简单的例子:判断一个数的奇偶性。你肯定知道这种判断方式:

if (n%2 == 1)
{
	// 奇数
}
else
{
	// 偶数
}

但也可以这么写:

if (n&1 == 1)
{
	// 奇数
}
else
{
	// 偶数
}

这是因为,1的补码的二进制是:00000000000000000000000000000001,如果拿一个数和它按位与,左边的31位全部会变成0。最右边的那一位如果是1,按位与之后就得到1;最右边的那一位如果是0,按位与之后就得到0。另外,奇数的最右边一位就是1,偶数最右边一位是0,这就对应上了。

也就是说:n&1这个式子能拿到n最右边的那一位。n最右边的那一位是1,n&1就是1;n最右边的那一位是0,n&1就是0。这一点非常重要!

当然,按位与还有一个作用:可以把某一位清0。比如,如果想把n的最右边一位清0,就n &= 0xfffffffe即可,也就是让n按位与11111111111111111111111111111110这样一个二进制。n左边的31位按位与1后不变,最右边1位不管是0还是1,按位与0后都得到0。

按位或(|)

按位或操作符(|)是一个二元操作符,用于对两个整数进行按位或运算。按位或操作符将两个整数补码的二进制进行按位比较,只有在相应位置上都为0时,结果才为0,否则结果为1。

例如,假设有两个整数a和b,它们在内存中存储的二进制(其实就是补码)分别为:

a = 00000000000000000000000010101100
b = 00000000000000000000000011001010

那么a | b的结果为:

a | b = 00000000000000000000000011101110

按位异或(^)

按位异或操作符(^)是一种二元操作符,用于对两个整数进行按位异或运算。按位异或运算是指对两个二进制数的每一位进行比较,如果相同则结果为0,如果不同则结果为1。可以简记为“找不同”。

详细点来说,就是以下的规则:

  1. 如果两个操作数的某一位都为0,则结果的该位也为0。
  2. 如果两个操作数的某一位都为1,则结果的该位也为0。
  3. 如果两个操作数的某一位一个为0,一个为1,则结果的该位为1。

有了按位与和按位或的基础,大家应该很好理解了。举个例子:

int a = 10; // 补码为1010
int b = 6; // 补码表示为0110
int c = a ^ b; // 按位异或运算,结果为1100,即12

在上面的代码中,变量a和b分别存储了10和6的补码,分别为0000000000000000000000000000101000000000000000000000000000000110。对这两个数进行按位异或运算,得到的结果为00000000000000000000000000001100,即12的补码。

掌握按位异或需要明白2个道理:

  1. 0^a=a, a^a=0
  2. 按位异或可以任意交换顺序。把同样的一堆数按位异或到一块,不管怎么交换这几个数的顺序,结果是不变的。

使用按位异或有一个很经典的例子:不创建临时变量,交换2个整数。

假设有2个整数:

int a = 10;
int b = 20;

如果创建临时变量,就这么写:

int tmp = a;
a = b;
b = tmp;

如果不创建临时变量,可以使用加减法交换:

a = a + b;
b = a - b;
a = a - b;

加减法交换的思路是:

  1. 先把2个数的和存在a中。
  2. 和减去b,得到最开始的a,放在b中。
  3. 和减去b中存放的最开始的a,得到最开始的b,放在a中,从而完成交换。

或者使用按位异或交换:

a = a ^ b;
b = a ^ b;
a = a ^ b;

思路是:

  1. 先把2个数异或的结果(a^b)放在a中。
  2. 这个结果异或b,得到(a^b)^b=a,放在b中(根据按位异或的2条性质可以推出这一点,朋友们开动脑筋想一想,忘记的可以向上翻)。
  3. 这个结果异或b中存储的一开始的a,得到b,放在a中,从而完成交换。

左移(<<)

C语言中的左移操作符(<<)是一种位运算符,它将一个数的二进制表示向左移动指定的位数,并在右侧填充零,简记为:左边丢弃,右边补0。左移操作符的语法是:value << n,其中,value是要进行左移操作的数,n是要左移的位数。

例如,假设value的二进制表示为00000000000000000000000000001010,执行value << 2操作后,结果为00000000000000000000000000101000,即将原来的二进制数向左移动了两位,最左边的2位丢了,右侧填充了两个零。

左移操作符的作用是将一个数乘以2的n次方,因为左移n位相当于将原数乘以2的n次方。

需要注意的是,左移操作符可能会导致数据溢出,因此在使用时需要谨慎。如果左移的位数超过了数据类型的位数,那么结果将是未定义的。此外,左移操作符如果作用于负数,会导致负数的符号位被丢弃。

右移(>>)

右移操作符(>>)是一种位运算符,用于将一个数的二进制补码向右移动指定的位数。右移操作符有两种类型:算术右移和逻辑右移。

  1. 算术右移(右边丢弃,左边补原来的符号位):当对整数进行右移操作时,算术右移会将最高位的符号位保留下来,即将符号位复制到右移后的空位上。例如,对于这样一个二进制序列11111111111111111111111111111111,进行算术右移1位,结果仍然是11111111111111111111111111111111,因为符号位1被复制到右移后的空位上。
  2. 逻辑右移(右边丢弃,左边补0):当对整数进行右移操作时,逻辑右移会将最高位的符号位忽略掉,即将右移后的空位上填充0。例如,对于这样一个二进制序列11111111111111111111111111111111,进行逻辑右移1位,结果为01111111111111111111111111111111,因为右移后的空位无论如何都会补0。

需要注意的是,右移操作符可能会导致数据溢出,因此在使用时需要谨慎。如果右移的位数超过了数据类型的位数,那么结果将是未定义的。此外,左移操作符如果作用于负数,算术右移和逻辑右移的结果是不一样的,算术右移会在高位补符号位(即1),逻辑右移会在高位补0。

总结

  1. 按位取反操作符(~)用于对内存中存储的二进制的每一位进行逻辑取反操作(!)。
  2. 按位与操作符(&)用于对内存中存储的二进制补码的每一位进行逻辑与运算(&&),按位或操作符(|)用于对内存中存储的二进制补码的每一位进行逻辑或运算(||)。
  3. 按位异或操作符(^)可以理解为“找不同”,如果两个整数在内存中存储的二进制补码的对应位不同,则结果为1,否则为0。
  4. 左移操作符(<<)的规则是:左边丢弃,右边补0。右移操作符(>>)的规则分为2种,算术右移的规则是:右边丢弃,左边补原来的符号位;逻辑右移的规则是:右边丢弃,左边补0。

感谢大家的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数电中需要注意的问题

逻辑函数表达式之间的相互转换 &#xff08;更多请详见PPT&#xff09;若题目要求用&#xff1a; 与非门实现逻辑函数&#xff0c;则画卡诺图圈出值为1的然后化简 或非门实现逻辑函数&#xff0c;则画卡诺图圈出值为0的然后化简 与或非门实现逻辑函数&#xff0c;则画卡诺图圈…

一文了解获得 Zebec Labs 投资的 Coral Finance,空投计划或在不久推出

在前不久&#xff0c;Zebec Labs宣布对链上衍生品协议Coral Finance进行150万美元的投资&#xff0c;以帮助该协议完成早期启动并&#xff0c;并在后续持续的为其提供孵化支持。Coral Finance将在不久部署在Nautilus Chain主网上。据了解&#xff0c;Coral Finance是Nautilus C…

51单片机电路基础

一.电平特性 单片机是一种数字集成芯片&#xff0c;数字电路中只有两种电平:高电平和低电平。 高电平: 5V低电平: 0V TTL电平信号被利用的最多是因为通常数据表示采用二进制&#xff0c;5V等价于逻辑“1”&#xff0c;0V等价于逻辑“0”。 TTL电平规定高电平输出电压>2.…

博客系统的后端设计(一) - 准备工作与设计数据库

文章目录 准备工作1. 创建一个 Maven 项目2. 引入依赖3. 创建目录结构 设计数据库 这次开始进行博客系统后端的进度&#xff0c;本篇博客讲的是 准备工作和 设计数据库两个步骤。 准备工作 1. 创建一个 Maven 项目 2. 引入依赖 在地址栏中搜素 https://mvnrepository.com/&a…

【微信小程序开发】【SpringBoot】解决真机调试中无法向后台请求数据的问题

前言 最近做了一个微信小程序SpringBoot的一个项目&#xff0c;在编译器中用localhost请求后台可以实现&#xff0c;但是在手机上进行真机调试就无法正确的从后台请求数据&#xff0c;问题已经解决&#xff0c;下面是我的一点经验 获取本机的ip地址&#xff08;ipv4&#xff09…

解决电脑由于找不到vcruntime140_1.dll,无法继续执行代码的方法

vcruntime140_1.dll是微软Visual C程序的运行库文件之一。它包含一些程序所需的函数和其他重要数据&#xff0c;这些程序通常是用Visual C编写的。如果缺少这个文件&#xff0c;可能会导致一些程序无法正常运行&#xff0c;电脑提示vcruntime140_1.dll无法继续执行代码&#xf…

C++三大特性—继承“名字搜索与默认成员函数”

继承中的类的作用域 每个类定义自己的作用域&#xff0c;在这个作用域中定义自己的成员。当存在继承关系时&#xff0c;派生类的作用域嵌套在基类的作用域之中。如果一个名字在派生类的作用域中无法解析&#xff0c;那么编译器将继续在外层的基类中寻找该名字的定义。 继承关系…

next.js博客搭建_初始化next项目(第一步)

文章目录 ⭐前言⭐next初始化TypeScript 开发项目安装react的ui框架&#xff08;tDesign&#xff09;设计布局 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本期给大家分享next项目搭建博客的开始。 背景 因为我的博客网站https://yongma16.xyz是基于vue2搭建的&am…

Centos7快速安装Kibana并连接ES使用

Elasticsearch 提供了一个名为 Kibana 的官方可视化界面。Kibana 是一个开源的数据可视化和管理工具&#xff0c;用于 Elasticsearch。它提供了丰富的功能&#xff0c;如仪表板、图表、地图等&#xff0c;帮助您更好地理解、搜索和可视化存储在 Elasticsearch 中的数据。 在 C…

营收、利润增速第一!海尔智家为何领跑?

“企业只有保持领先的能力&#xff0c;才有可能取得经济成果。” 管理学大师德鲁克曾如此强调。所谓“领先”&#xff0c;就是独一无二的、有价值的东西。利润&#xff0c;是企业在某个领域取得领先优势后&#xff0c;必然获得的回报。 这种“领先优势”&#xff0c;在各行业…

gpt4入口在哪里-怎么使用chatGPT4

Chat GPT 4.0 PLUS详细介绍 Chat GPT 4.0 PLUS是一款基于人工智能技术的自然语言处理模型&#xff0c;它是目前最先进的Chat GPT系列中的一员。Chat GPT 4.0 PLUS具有极高的精度和稳定性&#xff0c;可以帮助用户实现高品质、高效率的自然语言处理体验。下面详细介绍Chat GPT 4…

Python小姿势 - # Python爬虫技术

Python爬虫技术 许多人认为爬虫技术只能用于网页内容抓取&#xff0c;其实爬虫技术还可以用于更多的场景&#xff0c;比如数据挖掘、信息处理等。在这里&#xff0c;我们就来学习如何使用Python来编写爬虫。 首先&#xff0c;我们需要准备一个Python爬虫的开发环境。Python是一…

嵌入式Linux底层系统开发 +系统移植+内核文件系统(基础)

嵌入式Linux系统移植要点&#xff1a; 搭建交叉编译开发环境bootloader的选择和移植kernel的配置、编译、移植和调试根文件系统的制作 前两个要点通常芯片厂家提供。后边两个要点是公司的工作重点。 学习方法&#xff1a;先整体后局部&#xff0c;层层推进 如何编译—>如何…

idea修改 项目代码,浏览器页面不生效 解决方案

使用快捷键ctrl shift delete&#xff0c;清理浏览器缓存 1、问题描述 idea修改前端项目代码&#xff0c;运行谷歌浏览器不起作用。 我也试过 rebuild project, 重启idea&#xff0c;等方法都不管用。 再次运行谷歌浏览器&#xff0c;还是没有变化。 2、尝试了以下方法&am…

ElasticSearch学习随笔之分词算法

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 ElasticSea…

JUC之Java内置锁的核心原理

文章目录 JUC之Java内置锁的核心原理Java对象结构对象头对象体对齐字节 Mark Word的结构信息64位Mark Word的构成 偏向锁偏向锁的设置偏向锁的重偏向偏向锁的撤销偏向锁的膨胀 轻量级锁执行过程轻量级锁的分类普通自旋锁自适应自旋锁 重量级锁偏向锁、轻量级锁与重量级锁的对比…

探秘C语言经典题目:如何求解整数二进制中1的个数

本篇博客会讲解一道经典的题目&#xff1a;求一个整数二进制中1的个数。阅读本篇博客前&#xff0c;需要你对C语言如何进行二进制位操作有一定的了解&#xff0c;如果还不太了解的话&#xff0c;可以阅读一下我的这篇博客。 我们假设有一个int类型的整数n&#xff0c;我们知道…

12. Transformer(上)

P32 Transformer&#xff08;上&#xff09; 视频链接 P32 Transformer&#xff08;上&#xff09; Seq2seq应用: Seq2seq结构:

武忠祥老师每日一题||定积分基础训练(二)

仍是上一节中提到的基本思想 武忠祥老师每日一题||定积分基础训练&#xff08;一&#xff09; 在这个题中&#xff0c;M和N可以利用奇偶性判断。 如下&#xff1a; 从上可知&#xff0c; M ∫ − π 2 π 2 1 d x M\int_{-\frac{\pi}{2}}^{\frac{\pi}{2}}1\,{\rm d}x M∫−…

The 1st Universal Cup Stage 13: Iberia, Apr 22-23, 2023 题解

D. XOR Determinant You are given two arrays b and c of length n, consisting of non-negative integers. Construct n n matrix A as Aij bi ⊕ cj . Find the determinant of A modulo 998 244 353 考虑 A i j ∑ k b i , k c j , k p A_{ij}\sum_k b_{i,k}{c_{j,k}…