当前位置:网站首页>8.C语言——位操作符与位移操作符
8.C语言——位操作符与位移操作符
2022-07-06 09:19:00 【是王久久阿】
本文采用了《C Primer Plus》、《C陷阱与缺陷》以及比特科技的教学视频。
对C语言位操作符与位移操作符进行了详细讲解,为了加深印象,每一个知识点均有例题和实用讲解
目录
位操作符
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;//2
printf("1:a=%d,b=%d\n", a, b);
b = a ^ b;//1
printf("2:a=%d,b=%d\n", a, b);
a = a ^ b;//3
printf("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=2
return 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)为一个整型中的位数,还需要给终止符留位置,再+1
while (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后四位的值。
将该函数放到转换二进制例子中,运行结果如下:
通过观察发现,异或操作使后四位的值进行了颠倒。
边栏推荐
- How do architects draw system architecture blueprints?
- 记录:下一不小心写了个递归
- arduino+DS18B20温度传感器(蜂鸣器报警)+LCD1602显示(IIC驱动)
- 4.30动态内存分配笔记
- All in one 1405: sum and product of prime numbers
- IPv6 experiment
- 十分钟彻底掌握缓存击穿、缓存穿透、缓存雪崩
- What are the advantages of using SQL in Excel VBA
- GNSS positioning accuracy index calculation
- 几道高频的JVM面试题
猜你喜欢
Quickly generate illustrations
Arduino+ds18b20 temperature sensor (buzzer alarm) +lcd1602 display (IIC drive)
几道高频的JVM面试题
面渣逆袭:Redis连环五十二问,三万字+八十图详解。
Tyut Taiyuan University of technology 2022 introduction to software engineering summary
最新坦克大战2022-全程开发笔记-2
Application architecture of large live broadcast platform
Database operation of tyut Taiyuan University of technology 2022 database
如何保障 MySQL 和 Redis 的数据一致性?
西安电子科技大学22学年上学期《基础实验》试题及答案
随机推荐
TYUT太原理工大学2022数据库考试题型大纲
图书管理系统小练习
Counter attack of flour dregs: redis series 52 questions, 30000 words + 80 pictures in detail.
One article to get UDP and TCP high-frequency interview questions!
Differences and application scenarios between MySQL index clock B-tree, b+tree and hash indexes
凡人修仙学指针-2
Alibaba cloud microservices (I) service registry Nacos, rest template and feign client
String类
162. Find peak - binary search
MySQL 三万字精华总结 + 面试100 问,吊打面试官绰绰有余(收藏系列
Database operation of tyut Taiyuan University of technology 2022 database
List set map queue deque stack
Record: the solution of MySQL denial of access when CMD starts for the first time
最新坦克大战2022-全程开发笔记-2
Role movement in the first person perspective
学编程的八大电脑操作,总有一款你不会
13 power map
Iterable、Collection、List 的常见方法签名以及含义
A brief introduction to the database of tyut Taiyuan University of technology in previous years
Conceptual model design of the 2022 database of tyut Taiyuan University of Technology