IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 8.C语言——位操作符与位移操作符 -> 正文阅读

[C++知识库]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;//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后四位的值。

将该函数放到转换二进制例子中,运行结果如下:

?通过观察发现,异或操作使后四位的值进行了颠倒。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 22:37:15  更:2022-07-04 22:39:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/12 20:23:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码