二进制按位操作解析(C语言)

二进制按位操作解析(C语言)

C语言按位操作:包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>)六种。

1.首先我们了解一下"字节"、”位“的概念

C语言中有个sizeof方法是获取数据类型占几个字节的方法

int length = sizeof(int); // 输出:4(字节) 一个字节占8位,即4*8=32位 (注:此处位32位编译器,如16位编译器则应为2字节)


位是什么意思呢???

位就可以理解成开关,8位就是8个二进制的开关,开就是1关就是0.

二进制数"00001111"就代表8个开关的状态是“关关关关开开开开”,


所以有符号整型的最大值就是

int intMax = 2147483647; // 0 1111111 11111111 11111111 11111111

由于第一位表示数值正负(0表示正数,1表示负数),所以有符号int可以存储的最大数字为 0 1111111 11111111 11111111 11111111 => +2147483647 (10进制)

如果需要无符号整型,可以用如下方式表示,最大值为

unsigned int unIntMax = 4294967295; // 11111111 11111111 11111111 11111111 => +4294967295 (10进制)

短整型的长度为2字节,就不再举例了

int short shortlength = sizeof(short int);

2.按位与,按位或,按位异或

计算基础就不介绍了,应该很好理解,看不懂请自行百度

	// 3的二进制数为  : 0 0000000 00000000 00000000 00000011
	// 5的二进制数为  : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 按位与   3 & 5 : 0 0000000 00000000 00000000 00000001 => 1
	// 按位或   3 | 5 : 0 0000000 00000000 00000000 00000111 => 7
	// 按位异或 3 ^ 5 : 0 0000000 00000000 00000000 00000110 => 6

2.1按位与(&)的常见作用

我们先要做一些准备工作,以小写字母a为例。


char 类型保存的是字符对应的ascii码,是无符号整数

我们先来看看char类型的长度

int charlength = sizeof(char); // 输出 1,一个字节就相当于有8个0或1

通过查表我们知道,小写字母a对应的ASCII码 = > 97

97的二进制数为 = > 1100001


2.1.1作用1:将某几位,置零,其他位不变。

	01100001
	11111110
//---------------------------
	01100000

这样就将最低位,置0,得到96

96对应的ascii符号通过查表可得为单引号:’

2.1.2 作用2:取指定位

	01100001
	00001111
//---------------------------
	00000001

这样就可以只取最低4位

2.2 按位或(|)的常见作用

2.2.1 作用1:将某几位,置1,其他位不变。

同样以a为例

	01100001
	00000010
//---------------------------
	01100011

这样就将第二位,置1得到十进制99

99对应的ascii符号通过查表可得小写字母c

printf("a & 2 = %c 
", a | 2); // a & 2 = c

2.3 按位异或(^)的常见作用

2.3.1 作用1:将指定位翻转

以a为例

	01100001
	00001111
//---------------------------
	01101110

这样就将1~4位翻转,得到十进制110

110对应的ascii符号通过查表可得小写字母n

printf("a ^ 15 = %c 
", a ^ 0xf); // 输出a ^ 15 = n

3. 按位取反(~)

我们用10进制整型5举例

	// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 按位取反(非) ~5 : 1 1111111 11111111 11111111 11111001 => -6
	printf("按位取反 ~5 : %d 
", ~5);

这里我们发现一个很有趣的现象,为什么~5 = -6

那我们再多输出几个,找找规律

printf("按位取反 ~6 : %d 
", ~6); // -7
printf("按位取反 ~7 : %d 
", ~7); // -8
printf("按位取反 ~8 : %d 
", ~8); // -9

好像按位取反+1 就能得到原数的负数。

但是 1 1111111 11111111 11111111 11111001 在我们的理解中应该是 -2147483641 才对啊,为什么打印出来是-6呢?

没错,这个值也表示 -6,如果你学习过原码,补码,反码的知识,就知道

1 0000000 00000000 00000000 00000110 是-6的原码

1 1111111 11111111 11111111 11111001 是-6的反码

请一定要注意,按位取反是操作,是动词,反码是名词,是两个概念

请仔细观察上面的二进制数,发现什么特点没。

是不是符号位没变,余下取反?

没错,如果我们再将反码+1,就得到了-5

此时的反码就叫做补码

补上最后这个正1就得到了原数的相反数,是不是很有意思。


下面我们来尝试一下无符号整型取反

unsigned int unInt = ~5;
printf("无符号整型取反 ~5 : %d 
", ~unInt); //输出5

奇怪了?我们预期取反不应该是

5的二进制数为   : 00000000 00000000 00000000 00000101
------------------------------------------------------------
预期的结果      : 11111111 11111111 11111111 11111010 => 4294967290(10进制)

实话说我也不太清楚为什么这样,我只能猜测

因为人为规定:对于正数来说,其二进制原码,反码,补码均为相同的,为原码的形式;

难道C语言不能对无符号整型按位取反了吗???。这里先挖一个坑以后找时间去填上。

4. 左移运算(<<)

左移运算,高位舍弃,低位补0

相当于乘以2的效果

	// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 5左移1位 5<<1   : 0 0000000 00000000 00000000 00001010 => 10

	printf("左移1位 5<<1 : %d 
", 5 << 1 ); // 输出:10
	printf("左移2位 5<<2 : %d 
", 5 << 2 ); // 输出:20

5. 右移运算(>>)

右移运算,低位舍弃

无符号数高位补0

有符号数高位补符号位(即正数补0负数补1)

相当于除2的效果


// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
// --------------------------------------------------------
// 5右移1位 5>>1   : 0 0000000 00000000 00000000 00000010 => 2

printf("左移1位 5>>1 : %d 
", 5 >> 1);
printf("左移2位 5>>2 : %d 
", 5 >> 2);

由于舍弃了末位,小数的精度丢失




附:完整C代码片段

#include<stdio.h>

int main() {
	// 输出int类型长度
	int length = sizeof(int);  // 输出:4(字节),一个字节8位,即4*8=32位 //此处位32位编译器,如16位编译器则应为2字节
	
	int intMax = 2147483647; // 0 1111111 11111111 11111111 11111111
	// 由于第一位表示数值正负(0表示正数,1表示负数),所以有符号int可以存储的最大数字为 0 1111111 11111111 11111111 11111111 => +2147483647 (10进制)
	int intMax = 2147483647; // 0 1111111 11111111 11111111 11111111
	
	// 如果需要无符号整型,可以用如下方式表示
	unsigned int unIntMax = 4294967295; // 11111111 11111111 11111111 11111111

	int short shortlength = sizeof(short int); // 短整型的长度为2字节,就不再举例了

	printf("整型长度 : %d 
", length);
	printf("短整型长度 : %d 
", shortlength);

	
    // 按位与,按位或,按位异或的计算就不介绍了,应该很好理解,看不懂请自行百度

	// 3的二进制数为  : 0 0000000 00000000 00000000 00000011
	// 5的二进制数为  : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 按位与   3 & 5 : 0 0000000 00000000 00000000 00000001 => 1
	// 按位或   3 | 5 : 0 0000000 00000000 00000000 00000111 => 7
	// 按位异或 3 ^ 5 : 0 0000000 00000000 00000000 00000110 => 6
	int res = 3 & 5;
	printf("3 & 5 = %d 
", res);
	res = 3 | 5;
	printf("3 | 5 = %d 
", res);
	res = 3 ^ 5;
	printf("3 ^ 5 = %d 
", res);


	// 下面介绍按位与(&)的常见作用

	// char 类型保存的是字符对应的ascii码,是无符号整数
	int charlength = sizeof(char);
	printf("字符型长度 : %d 
", charlength);// 输出 1
	// a对应的ASCII码 = > 97
	// 97的二进制数为 = > 1100001
	char a = "a";
	printf("a->ascii = %d 
", a); // 输出 :a->ascii = 97 => 01100001

	/*
		作用1:将某几位置零,其他位不变。

		01100001
		11111110
		---------
		01100000

		这样就将最低位,置0,得到96
		96对应的ascii符号通过查表可得为单引号:`
	*/
	printf("a & 0xfe = %c 
", a & 0xfe);

	/*
		作用2:取指定位

		01100001
		00001111
		----------
		00000001

		这样就可以只取最低4位
	*/


	// 下面介绍按位或(|)的常见作用

	/*
		作用1:将某一位置1,其他位不变。

		同样以a为例

		01100001
		00000010
		----------
		01100011

		这样就将第二位,置1得到十进制99
		99对应的ascii符号通过查表可得小写字母c
	*/
	printf("a & 2 = %c 
", a | 2);


	// 下面介绍按位异或(^)的常见作用

	/*
		作用1:将指定位翻转

		以a为例

		01100001
		00001111
		----------
		01101110

		这样就将1-4位翻转,得到十进制110
		110对应的ascii符号通过查表可得小写字母n
	*/
	printf("a ^ 15 = %c 
", a ^ 0xf);

	// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 按位取反(非) ~5 : 1 1111111 11111111 11111111 11111001 => -6
	printf("按位取反 ~5 : %d 
", ~5);
	// 这里我们发现一个很有趣的现象,为什么~5 = -6
	// 那我们再多输出几个,找找规律
	printf("按位取反 ~6 : %d 
", ~6); // -7
	printf("按位取反 ~7 : %d 
", ~7); // -8
	printf("按位取反 ~8 : %d 
", ~8); // -9

	// 好像按位取反+1 就能得到原数的负数。
	// 但是 1 1111111 11111111 11111111 11111001 在我们的理解中应该是 -2147483641 才对啊,为什么打印出来是-6呢?
	
	// 那我们来想想按你的理解 -6 应该怎么表示?
	// 最高位表示符号,负数就应该为1,6对应的二进制应该是 110,中间补0,就应该是 1 0000000 00000000 00000000 00000110

	// 没错,这个数也表示 -6,如果你学习过原码,补码,反码的知识,就知道
	// 1 0000000 00000000 00000000 00000110 是-6的原码
	// 1 1111111 11111111 11111111 11111001 是-6的反码
	
	// 请一定要注意,按位取反是操作,是动词,反码是名词,是两个概念

	// 请仔细观察上面的二进制数,发现什么特点没。
	// 是不是符号位没变,余下取反?
	// 没错,如果我们再将反码+1,就得到了-5,此时的反码就叫做补码,补上最后这个正1就得到了原数的相反数,是不是很有意思。

	// 下面我们来尝试一下无符号整型取反
	unsigned int unInt = ~5;
	printf("无符号整型取反 ~5 : %d 
", ~unInt); //输出5
	// 奇怪了?我们预期取反不应该是
	// 5的二进制数为   : 00000000 00000000 00000000 00000101
	// ------------------------------------------------------------
	// 预期的结果      : 11111111 11111111 11111111 11111010 => 4294967290(10进制)

	// 实话说我也不太清楚为什么这样,我只能猜测
	// 因为人为规定:对于正数来说,其二进制原码,反码,补码均为相同的,为原码的形式;

	// 难道C语言不能对无符号整型按位取反了吗???。这里先挖一个坑以后找时间去填上。



	// 左移运算,高位舍弃,低位补0
	// 相当于乘以2的效果
	// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 5左移1位 5<<1   : 0 0000000 00000000 00000000 00001010 => 10

	printf("左移1位 5<<1 : %d 
", 5 << 1 );
	printf("左移2位 5<<2 : %d 
", 5 << 2 );


	// 右移运算,低位舍弃,无符号数高位补0,有符号数高位补符号位(即正数补0负数补1)
	// 相当于除2的效果
	// 5的二进制数为   : 0 0000000 00000000 00000000 00000101
	// --------------------------------------------------------
	// 5右移1位 5>>1   : 0 0000000 00000000 00000000 00000010 => 2
	printf("左移1位 5>>1 : %d 
", 5 >> 1);
	printf("左移2位 5>>2 : %d 
", 5 >> 2);
	// 由于舍弃了末位,小数的精度丢失

	return 0;
}


hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 二进制按位操作解析(C语言)