8.C语言——位操作符与位移操作符
本文采用了《C Primer Plus》、《C陷阱与缺陷》以及比特科技的教学视频。
对C语言位操作符与位移操作符进行了详细讲解,为了加深印象,每一个知识点均有例题和实用讲解
目录
位操作符
1.按(二进制)位与 &
2.按(二进制)位或 |
3.按(二进制)位异或 ^
4.按(二进制)位取反:~
位操作符用法
1.掩码
2.打开位(设置位)
3.关闭位(清空位)
位移操作符
1.左移操作符
2.右移操作符
位移操作符用法
1.用位移运算符把整数转换成二进制形式
2.转换某一个值的后n位
位操作符
1.按(二进制)位与 &
规则:对应位都为1时,结果为1,否则为0。
例如:
int a = 1,b = 3;
00000000 00000000 00000000 00000001 //a
00000000 00000000 00000000 00000011 //b
00000000 00000000 00000000 00000001 //a&b=1
数值在计算机中存储时,都是以补码的形式进行存储的,正整数(包括0)的原码、反码、补码都相同;而负数的原码、反码、补码则需要计算:通过将原码取反再加1,可以得到一个整数的补码;同理将一个补码进行取反加1,也可以得到这个数的原码。
#include<stdio.h>
int main()
{int a = 1;int b = 3;int c = a & b;printf("a=%d,b=%d,c=%d\n", a, b,c);return 0;
}
2.按(二进制)位或 |
规则:对应位只要有一个为1,结果就为1,否则为0。
例如:
int a = 1,b = 3;
00000000 00000000 00000000 00000001 //a
00000000 00000000 00000000 00000011 //b
00000000 00000000 00000000 00000011 //a|b=3
#include<stdio.h>
int main()
{int a = 1;int b = 3;int c = a | b;printf("a=%d,b=%d,c=%d\n", a, b,c);return 0;
}
3.按(二进制)位异或 ^
规则:对应位只有一位为1,结果为1,否则为0。
例如:
int a = 1,b = 3;
00000000 00000000 00000000 00000001 //a
00000000 00000000 00000000 00000011 //b
00000000 00000000 00000000 00000010 //a^b=2
#include<stdio.h>
int main()
{int a = 1;int b = 3;int c = a ^ b;printf("a=%d,b=%d,c=%d\n", a, b,c);return 0;
}
练习:利用异或操作符交换两个变量的值(不创建额外变量)
#include<stdio.h>
int main()
{int a = 1;int b = 3;printf("a=%d,b=%d\n", a, b);a = a ^ b;//2printf("1:a=%d,b=%d\n", a, b);b = a ^ b;//1printf("2:a=%d,b=%d\n", a, b);a = a ^ b;//3printf("3:a=%d,b=%d\n", a, b);return 0;
}
4.按(二进制)位取反:~
假设a的类型是int,a被赋值为1;
a:
原码:00000000 00000000 00000000 00000001
反码:00000000 00000000 00000000 00000001
补码:00000000 00000000 00000000 00000001
对其将其按位取反,原来是0的地方变成1,原来是1的地方变成0,那么取反之后则变成了:
~a:
补码:11111111 11111111 11111111 11111110
反码:10000000 00000000 00000000 00000001 //符号位不变,其他位按位取反
原码:10000000 00000000 00000000 00000010 //反码+1,~a = -2
#include<stdio.h>
int main()
{int a = 1;int b = ~a;printf("a=%d b=%d \n", a,b);return 0;
}
注:位操作本身不会改变变量的值,而是创建一个新值,想改变变量的值可以采用:&=、|=、^=、~=。
位操作符用法
1.掩码
按位与运算符常用与掩码(mask)。所谓掩码指的是一些设置为开(1)或关(0)的位组合。
定义一个符号常量MASK为2,其后八位在内存中的形式为00000010,这个MASK称掩码。
再定义一个变量a,赋值为7,其后八位在内存中的形式为00000111。
#include<stdio.h>
#define MASK 2
int main()
{int a = 7;a &= MASK;printf("a=%d\n", a);//a=2return 0;
}
a&MASK = 00000010,最右侧为0号位,最左侧为7号位。在按位与的过程中,除了1号位的1显示了,剩下的所有位都显示为0。这个过程叫做“使用掩码”,因为掩码中的0隐藏了变量a中相应的位。下面是按位与的常见用法:
a &= 0xff;
//或者
a &= 0377;
0xff为16进制,其二进制形式为11111111,八进制形式是0377。这个掩码会保持变量a中的后八位不变,其他位都设置为0。无论a原来是多少位,最终的值都被修改为1个8位字节,8就是该掩码的宽度。
2.打开位(设置位)
有时需要打开一个值中的特定位,同时保持其他位不变。例如:为了打开内置扬声器,必须打开1号位,同时保持其他位不变,这种情况可以用按位或运算符(|)。
设置一个变量a和MASK(只有一号位为1)为例:
假设a为00000111,MASK为00000010,那么a |= MASK =00000111。
MASK中为1的位,a中也为1;MASK中为0的位,a中保持不变。
倘若要同时打开1、3、5号位,那么可以将MASK设置为:00101010,根据不同的需要来设置MASK。
3.关闭位(清空位)
与打开位类似,有时候也需要在不影响其他位的情况下关闭指定位。假设要关闭的变量a中的1号位,可以这样做:
定义符号常量MASK只有1号位为1:00000010
#include<stdio.h>
#define MASK 2
int main()
{int a = 6;printf("改变前:%d\n", a);a &= ~MASK;printf("改变后:%d\n",a);return 0;
}
6的二进制后8位为:00000110
4的二进制后8位为:00000100
MASK中为1的位在结果中清空为0,其他位在结果中保持不变。
位移操作符
位移操作符的操作数只能是整数。
1.左移操作符
规则:左边舍弃,右边补零。
例如:
int a = 5;在C语言中,正整数(包括0)的原码、反码、补码都相同,如下所示:
a:
原码:00000000 00000000 00000000 00000101
反码:00000000 00000000 00000000 00000101
补码:00000000 00000000 00000000 00000101
a << 1:
补码:0 00000000 00000000 00000000 000001010
右边补一个0,最左边的0抛弃,a << 1的结果为10。
#include<stdio.h>
int main()
{int a = 1;int a2 = a << 1;printf("a=%d,a2=%d\n", a, a2);int b = 5;int b2 = b << 1;printf("b=%d,b2=%d\n", b, b2);return 0;
}
通过测试发现:
- 左移操作符可以达到*2的效果,左移一位,数值*2;左移两位,数值*4,以此类推。
- 位移操作符不改变原数值,而是创建了一个可以使用或赋值的新值。
2.右移操作符
规则:右移操作符分为逻辑位移和算数位移两种规则。
逻辑位移:左边补0,右边舍弃。
算数位移:左边补原数值符号位,右边舍弃。
算数位移可以保证新值与原数值的符号相同,较逻辑位移更加科学,大多数编译器都采用此规则。
例如:
int a = -5;
原码:10000000 00000000 00000000 00000101
反码:11111111 11111111 11111111 11111010 //符号位不变,其他位按位取反
补码:11111111 11111111 11111111 11111011 //反码+1得到补码
a >> 1:
补码:11111111 11111111 11111111 11111101 1 //左边补原数值符号位1,右边舍弃1
反码:10000000 00000000 00000000 00000010
原码:10000000 00000000 00000000 00000011 //a>>1 = -3
#include<stdio.h>
int main()
{int a = 1;int a2 = a >> 1;printf("a=%d,a2=%d\n", a, a2);int b = -5;int b2 = b >> 1;printf("b=%d,b2=%d\n", b, b2);return 0;
}
注:对于位移运算符,只有移动正整数位才是有意义的!移动负数、小数等均是标准未定义的,无意义的。
位移操作符用法
1.用位移运算符把整数转换成二进制形式
#include<stdio.h>
#include<limits.h>void itobs(int n, char* ps);//计算二进制
void print_bstr(const char* str);//打印二进制int main()
{printf("请输入数字 or 按其他任意键退出\n");int number = 0;char bin_str[CHAR_BIT * sizeof(int) + 1]={0};//CHAR_BIT表示char中的位数,返回值为8//8 * sizeof(int)为一个整型中的位数,还需要给终止符留位置,再+1while (scanf("%d", &number) == 1)//输入非法字符时跳出循环{itobs(number, bin_str);//计算二进制printf("%d的二进制序列为:>", number);print_bstr(bin_str);//打印二进制putchar('\n');}return 0;
}//计算二进制
void itobs(int n, char* ps)
{int i = 0;const static int size = CHAR_BIT * sizeof(int);for (i = size - 1; i >= 0; i--, n >>= 1){*(ps + i) = (01 & n) + '0';//为了更加贴合计算机,采用01&n,01为8进制序列形式,1&n亦可,//因为数组的形式是char类型,为了表示0或1,还需要加上字符0的值}*(ps + size) = '\0';//最后一个位置赋上终止符
}//打印二进制序列
void print_bstr(const char* str)
{int i = 0;while (*(str + i))//不是空字符{putchar(*(str + i));if (++i % 8 == 0)//为了方便观看,每8个空一格putchar(' ');}
}
- 上述代码中, CHAR_BIT是头文件<limits.h>中的宏定义,该宏表示char中的位数,返回值为8。
- 表达式sizeof(int)求一个整型的大小,结果是4个字节;表达式CHAR_BIT * sizeof(int)表示一个整型的位数。
- 函数itobs中的for循环是从最后一位开始,计算二进制的结果,每次循环结束,n>>1右移一位,进行前一位的计算。
- 算式01&n的结果只有两种:0或1,为了能在char类型的数组中体现出0、1,还需要将其计算结果加上字符0的大小(‘0’)。
- 函数中的操作均是利用指针的形式进行运算,所以无需返回值。
最后运算结果如下:
2.转换某一个值的后n位
按位取反操作符可以切换某个值的所有位,但无法切换选中的特定位。假设想切换某个值中的后四位,该如何做?
可以利用按位异或(^)完成这项任务,首先创建一个掩码,然后将掩码的后四位都设置成1,将想更改的值与掩码进行异或操作,便可以切换该数值的后四位了。
int invert(int num, int bits)
{int mask = 0;int val = 1;while (bits-- > 0){mask |= val;val <<= 1;}return num ^= mask;
}
- 上式中,用num接收想要更改的值,用bits接收想要更改的位数。
- mask为掩码,val设定为1,将mask与val进行或等操作(|=),可以将mask最后一位更改成1,然后将val左移一个单位,再次进行循环,更改mask倒数第二位的值,以此类推,直到循环结束。
- 假设bits=4,那么跳出while循环后,mask在计算机存储的值为000011111(后八位),将其与num进行异或,即可切换num后四位的值。
将该函数放到转换二进制例子中,运行结果如下:
通过观察发现,异或操作使后四位的值进行了颠倒。
8.C语言——位操作符与位移操作符相关推荐
- 操作符(一)(算数操作符,位移操作符,位操作符)
目录: 1.算术操作符 2.位移操作符 (1) << 左移操作符(高位丢掉,低位补0): eg:将1左移1位 (2) >> 右移操作符: eg1:将1右移一位 eg2:将 ...
- 【C语言初阶】带你轻松玩转所有常用操作符(1) ——算数操作符,移位操作符,位操作符
君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,最近要准备期末复习了,可能更新的就不会那么频繁了,下个星期恢复正常更新. 操作符详解1 前言 一.操作符的分类 ...
- 【C语言】第五章 - 操作符
目录 1.算术操作符 2.移位操作符 左移 右移 3. 位操作符 & 按位与 | 按位或 ^ 按位异或 ~ 按位取反 4.赋值操作符 5.单目操作符 6.关系操作符 7.逻辑操作符 8.条件 ...
- 一元操作符、强制类型转换表达式、乘除操作符、加法操作符、移位操作符、关系操作符、判等操作符、位操作符与逻辑操作符、条件与操作符、条件或操作符--运算机制与返回值
操作符处理及返回值 先遣提示 一元操作符 前缀递增操作符 ++ 前缀递增操作符 - - 一元加号操作符 + 关于一元数值提升或二元数值提升 可以看这篇博客 一元减号操作符 - 这里涉及了 原码,反码, ...
- 位移操作符 <<左移 与 >>右移 的基本逻辑
1:<< 左移操作符 2:>> 右移操作符 (注:位移操作符的操作数只能是整数) << 左移操作符 与 >> 右移操作符 都是移二进制位操作 ...
- 抽丝剥茧C语言(中阶)操作符+练习
操作符祥解 导语 操作符分类: 1.算术操作符 2. 移位操作符 2.1 左移操作符 2.2 右移操作符 3. 位操作符 4. 赋值操作符 5. 单目操作符 5.1 单目操作符介绍 5.2 sizeo ...
- R语言dataframe创建新的特征(变量)、行加和特征、行均值特征(基于加减乘除、指数、模数等操作符)、创建新的特征(变量)、生成编码特征(基于比较操作符、逻辑操作符)
R语言dataframe创建新的特征(变量).行加和特征.行均值特征(基于加减乘除.指数.模数等操作符).为dataframe创建新的特征(变量).生成编码特征(基于比较操作符.逻辑操作符) 目录
- Java快速入门学习笔记3 | Java语言中的表达式与操作符
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- C++语言学习(八)——操作符重载
C++语言学习(八)--操作符重载 一.操作符重载基础 1.操作符重载的语法 通过operator关键字可以定义特殊的函数,operator本质是通过函数重载操作符. Type operator op ...
最新文章
- 三种常见嵌入式设备通信协议
- python virtualenv conda_在vscode中启动conda虚拟环境的思路详解
- opencv 图像平移、缩放、旋转、翻转 图像仿射变换
- CentOS6.7 mysql 主从配置
- 如何保持自己 fork 的项目和原始项目同步
- 三菱GXWorks2 变换梯形图
- maya如何查看资源大纲_干货|Maya入门到精通完全自学教程大纲(建模篇)
- 网课答案搜题API接口使用
- python 化学反应_有意思的Python:用python配平化学方程式
- RemoveWGA升级了
- Echarts地图实现点击某地区跳转到指定页面
- 毕业了,开始新的生活!
- python怎么批量爬取图片_python批量爬取网络图片
- 理解深度学习中的学习率
- 北京某牧场管理企业——牧场智慧管理项目
- 中国无叶片安全风扇市场趋势报告、技术动态创新及市场预测
- 数据中心服务器大战:思科vs惠普vs…
- String类中的trim()方法实现
- Quartus II三种方式实现D触发器及时序仿真
- Liunx——文件权限操作