详解C语言中的6个位操作符:按位取反、按位与、按位或、按位异或、左移、右移

news2024/12/26 1:18:05

在这里插入图片描述

本篇博客会讲解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

按位异或(^)

C语言中的按位异或操作符(^)是一种二元操作符,用于对两个整数进行按位异或运算。按位异或运算是指对两个二进制数的每一位进行比较,如果相同则结果为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/483351.html

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

相关文章

ESP32设备驱动-TSL2591数光转换器驱动

TSL2591数光转换器驱动 文章目录 TSL2591数光转换器驱动1、TSL2591介绍2、硬件准备3、软件准备4、驱动实现1、TSL2591介绍 TSL2591 是一款非常高灵敏度的光数字转换器,可将光强度转换为能够直接 I2C 接口的数字信号输出。 该器件在单个 CMOS 集成电路上结合了一个宽带光电二极…

如何用ChatGPT做会议总结?

该场景对应的关键词库&#xff08;12个&#xff09;&#xff1a; 会议主题、参与人员、讨论议题、关键观点、决策、时间、地点、修改要求、文本格式、语言风格、列表、段落。 提问模板&#xff08;3个&#xff09;&#xff1a; 第一步&#xff1a;用飞书会议等软件整理好会议…

代码随想录算法训练营第四十九天| 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II

文章目录 121. 买卖股票的最佳时机122.买卖股票的最佳时机II 121. 买卖股票的最佳时机 为什么定义dp数组为二维数组&#xff1f; dp数组定义&#xff0c;dp(i)[0] 表示第i天持有股票所得最多现金&#xff0c;dp(i)[1]表示第i天不持有股票的状态&#xff08;未必当前卖出&#x…

《Netty》从零开始学netty源码(五十)之PoolArena的内存分配

目录 tcacheAllocateSmall()tcacheAllocateNormal()newChunk() allocateHuge()newUnpooledChunk() ​PoolArena根据请求大大小主要分配三中类型的内存&#xff0c;小于28KB的分配PoolSubpage&#xff0c;28KB~4MB的分配池化的PoolChunk&#xff0c;4MB以上的分配非池化的内存​…

【C++ 学习 ②】- 类和对象(上)

目录 一、 面向对象的基本理念 1.1 - 什么是对象&#xff1f; 1.2 - 类和对象 1.3 - 面向对象的五条原则 1.4 - 面向过程 vs 面向对象 二、C 中的结构体 三、类的定义 3.1 - 类的两种定义方式 3.2 - 成员变量的命名规范 四、类的访问限定符和封装 4.1 - 访问限定符 …

SPFA + 链式前向星建图【附Java模板】

SPFA算法是什么&#xff1f;它能解决什么样的问题&#xff1f; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 数据结构与算法专栏的文章图文并茂&#x1f995;生动形象&#x1f996;简…

P3029 [USACO11NOV]Cow Lineup S 双指针 单调队列

“五一”小长假来了趟上海&#xff0c;在倒数第二天终于有时间做了一会儿题目&#xff0c;A了之后过来写一篇题解 【问题描述】 农民约翰雇一个专业摄影师给他的部分牛拍照。由于约翰的牛有好多品种&#xff0c;他喜欢他的照片包含每个品种的至少一头牛。 约翰的牛都站在一条沿…

[MAUI]模仿iOS应用多任务切换卡片滑动的交互实现

文章目录 原理创建布局创建分布函数创建动效创建绑定数据细节调整首张卡片的处理为卡片添加裁剪跳转到最后一张卡片 项目地址 看了上一篇博文的评论&#xff0c;大家对MAUI还是比较感兴趣的&#xff0c;非常感谢大家的关注&#xff0c;这个专栏我争取周更&#x1f609;。 App之…

华为OD机试真题(Java),数组拼接(100%通过+复盘思路)

一、题目描述 现在有多组整数数组,需要将它们合并成一个新的数组。 合并规则,从每个数组里按顺序取出固定长度的内容合并到新的数组中,取完的内容会删除掉,如果该行不足固定长度或者已经为空,则直接取出剩余部分的内容放到新的数组中,继续下一行。 二、输入描述 第一…

【Python入门篇】——PyCharm的基础使用

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

Mysql Sharding-JDBC读写分离

0 课程视频 深入Sharding-JDBC分库分表从入门到精通【黑马程序员】_哔哩哔哩_bilibili 1 基本概念 1.1应用逻辑 1.1.1 msyql 多库 多表 多服务器 1.1.2 通过Sharding-JDBC jar包->增强JDBC 访问多数据源 -> 自动处理成一个数据源 1.1.3 使用数据的人 -> 使用Sh…

Python+Yolov5墙体裂缝识别

程序示例精选 PythonYolov5墙体裂缝识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonYolov5墙体裂缝识别>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c…

C++类的默认成员函数

文章目录 默认函数构造函数和析构函数构造函数析构函数 拷贝构造函数运算符重载赋值运算符重载赋值运算符重载和构造函数 默认函数 什么是默认函数&#xff1f; 默认函数就是当你使用这个类对象时,这个类会自动调用的函数C中有六个默认成员函数,并且作用各不相同,下面我们来一…

2 ROS2话题通讯基础

2 ROS2话题通讯基础 2.1 ROS2话题通讯介绍2.2 ROS2常用的消息类型介绍2.2.1 std_msgs消息类型2.2.2 geometry_msgs消息类型 2.3 使用C/C创建基础消息类型的话题通讯2.3.1 创建C/C发布话题信息的功能包并配置VSCode环境2.3.2 编写ROS2发布话题节点CPP文件2.3.3 配置C/C发布话题功…

数据结构学习记录——堆的插入(堆的结构类型定义、最大堆的创建、堆的插入:堆的插入的三种情况、哨兵元素)

目录 堆的结构类型定义 最大堆的创建 堆的插入 堆的插入的三种情况 代码实现 哨兵元素 堆的结构类型定义 #define ElementType int typedef struct HNode* Heap; /* 堆的类型定义 */ struct HNode {ElementType* Data; /* 存储元素的数组 */int Size; /* 堆中…

『python爬虫』08. 数据解析之bs4解析(保姆级图文)

目录 1. 安装bs4模块find()findall() 2. 爬取信息测试总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 1. 安装bs4模块 pip install bs4 pip install-i https://pypi.tuna.tsinghua.edu.cn/simplebs4如果遇到报…

[网络原理] HTTP协议

要珍惜时间呀 文章目录 1. HTTP协议概念2. HTTP协议格式2.URL3. GET与POST方法3.1 GET方法3.2 POST方法3.3 GET与POST的区别 1. HTTP协议概念 HTTP协议是应用层协议,TCP/IP协议为传输层协议,负责传输数据.而HTTP协议相当于对传输的数据据怎样处理和使用进行说明. 每次,我们访问…

Mininet+Ryu安装教程

最近要做一个Mininet的网络环境&#xff0c;网络设备由Mininet来模拟&#xff0c;SDN控制器用Ryu来做&#xff0c;为了避免每次重新做再去翻查资料&#xff0c;我在这里系统地整理一遍 硬件需求 我在 VMWare Workstation 16 Player虚拟机上运行的Ubuntu 22.04.1 硬件需求内存…

供应链 | 需求不确定情况下的物料需求规划: 基于随机优化的研究

作者&#xff1a;Simon Thevenin, Yossiri Adulyasak, Jean-Francois Cordeau​ 引用&#xff1a;Thevenin S, Adulyasak Y, Cordeau J F. Material requirements planning under demand uncertainty using stochastic optimization[J]. Production and Operations Management,…

react的项目实战 2

入口文件引入了app这个组件 app这个组件又引入了header这个组件。 然后外面引入这个组件 进行页面的显示 它不会影响到其他页面的组件的样式。 ​​​​​​​