当前位置:网站首页>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后四位的值。
将该函数放到转换二进制例子中,运行结果如下:
通过观察发现,异或操作使后四位的值进行了颠倒。
边栏推荐
- Tyut Taiyuan University of technology 2022 introduction to software engineering
- Shortest Hamilton path (pressure DP)
- Design a key value cache to save the results of the most recent Web server queries
- TYUT太原理工大学2022数据库考试题型大纲
- 12 excel charts and arrays
- Record: the solution of MySQL denial of access when CMD starts for the first time
- 学编程的八大电脑操作,总有一款你不会
- MySQL limit x, -1 doesn't work, -1 does not work, and an error is reported
- MYSQL索引钟B-TREE ,B+TREE ,HASH索引之间的区别和应用场景
- 继承和多态(下)
猜你喜欢
Record: the solution of MySQL denial of access when CMD starts for the first time
9.指针(上)
2022 National Games RE1 baby_ tree
Rt-ppp test using rtknavi
抽象类和接口
How do architects draw system architecture blueprints?
如何保障 MySQL 和 Redis 的数据一致性?
Fairygui bar subfamily (scroll bar, slider, progress bar)
XV Function definition and call
121道分布式面试题和答案
随机推荐
【话题终结者】
167. Sum of two numbers II - input ordered array - Double pointers
Record: solution of 404 error of servlet accessing database in dynamic web project
Role movement in the first person perspective
Interview Essentials: talk about the various implementations of distributed locks!
Arduino+ds18b20 temperature sensor (buzzer alarm) +lcd1602 display (IIC drive)
阿里云微服务(一)服务注册中心Nacos以及REST Template和Feign Client
Tyut Taiyuan University of technology 2022 introduction to software engineering summary
TYUT太原理工大学2022数据库大题之数据库操作
Alibaba cloud side: underlying details in concurrent scenarios - pseudo sharing
Network layer 7 protocol
(ultra detailed onenet TCP protocol access) arduino+esp8266-01s access to the Internet of things platform, upload real-time data collection /tcp transparent transmission (and how to obtain and write L
Summary of multiple choice questions in the 2022 database of tyut Taiyuan University of Technology
TYUT太原理工大学2022软工导论简答题
Fairygui bar subfamily (scroll bar, slider, progress bar)
Decomposition relation model of the 2022 database of tyut Taiyuan University of Technology
Fundamentals of UD decomposition of KF UD decomposition [1]
Small exercise of library management system
Design a key value cache to save the results of the most recent Web server queries
Database operation of tyut Taiyuan University of technology 2022 database