当前位置:网站首页>8.C语言——位操作符与位移操作符

8.C语言——位操作符与位移操作符

2022-07-06 09:19:00 是王久久阿

本文采用了《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

反码: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后四位的值。

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

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

原网站

版权声明
本文为[是王久久阿]所创,转载请带上原文链接,感谢
https://blog.csdn.net/why1472587/article/details/125515757