【初阶C语言】操作符1--对二进制的操作

前言:本节内容介绍的操作符,操作的对象是二进制位。所以前面先介绍整数的二进制位

一、二进制位介绍

1.二进制介绍

(1)整数的二进制表示形式有三种:原码、反码和补码。

(2)原码、反码和补码的长度有数据类型来决定,如整数,就是四个字节,转化后是三十二位比特位,所以一个整数写成二进制序列的时候,是32个bit位。

(3)数据是以补码的形式在内存中存储的。

(4)正负数原码、反码和补码不一样

2.正数三码的介绍

(1)正数的原码、反码和补码都一样,不需要计算,可以直接得出来。

(2)举例

7,我们先把7转化成二进制数:111;由于整数的二进制序列是32位,所以其他我需要补0,就写成这样

原码:00000000 00000000 00000000 00000111

反码:00000000 00000000 00000000 00000111

补码:00000000 00000000 00000000 00000111

3,二进制数为:11 二进制序列:

原码:00000000 00000000 00000000 00000011

反码:00000000 00000000 00000000 00000011

补码:00000000 00000000 00000000 00000011

3.负数三码的介绍

我们接下来重点介绍负数的原码、反码和补码

++(1)原码++

对于有符号的整数来说,最高位的一位是符号位;符号位是1表示负数,符号位是0表示正数。若是无符号数,没有符号位,所有位都是有效位。数字0则当成无符号数来看待。

正数:

负数:

对于有符号数来说,正负数原码的差异就是在最高位。

++(2)反码++

1.反码,顾名思义就是取反。

定义:反码是在原码的基础上,除了符号位,其他按位取反,1的话就写成0,0就写成1。

2.-7的反码

原码:10000000 00000000 00000000 00000111

反码:11111111 11111111 11111111 11111000

++(3)补码++

1.定义:补码则是在反码的基础上+1。

2.-7的补码

原码:10000000 00000000 00000000 00000111

反码:11111111 11111111 11111111 11111000

补码:11111111 11111111 11111111 11111001

而补码,就是数据在内存中的存储形式。所以整数在计算的时候使用的也是补码。

总结:三码的转换规则:原码----(取反)--->反码-----(+1)---->补码

补码----(-1)--->反码----(取反)--->原码

补码----(取反,+1)--->原码

二、移位操作符

前言:移位操作符包括左移操作符(<<)和右移操作符(>>)

1.左移操作符(<<)

(1)常用口诀:左边丢弃,右边补0

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 7;
	int b = a << 1;//a向左移动一位

	printf("%d\n",a);//a的本质没有改变
	printf("%d\n",b);
	return 0;
}

代码结果:

由上面很清楚的就可以知道,其实a的值还是-7,没有改变。

代码演示2:

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = -7;//将a的值改成负数
	int b = a << 1;
    int c = a << 2;
	printf("%d\n",a);
	printf("%d\n",b);
    printf("%d\n",c);
	return 0;
}

运行结果:

两次代码演示初步总结:左移一位,结果在原数据基础上×2;左移两位,结果在原数据基础上×3;但原数据的值不会改变。

(3)左移符号解析

左移符号,移动的是补码,但是打印的时候打印的是原码。

正数左移:7

原码:00000000 00000000 00000000 00000111

反码:00000000 00000000 00000000 00000111

补码:00000000 00000000 00000000 00000111

正数的左移比较容易看出来,因为原码、反码和补码都相同

负数右移:-7

原码:10000000 00000000 00000000 00000111

反码:11111111 11111111 11111111 11111000

补码:11111111 11111111 11111111 11111001

总结:

无论正数还是负数,左移的结果都会×2。

记忆口诀:左边丢弃,右边补0。

2.右移操作符(>>)

(1)右移操作符分类

右移操作符包括:算术右移和逻辑右移

逻辑右移:右边直接丢弃,左边补0

算术右移:右边直接丢弃,左边补原符号位(正数补0,负数补1)-----------常用选择

我们下面的例子也都用算术右移来演示

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 10;
	int b = a >> 1;
	printf("%d\n", a);
	printf("%d\n", b);

	int c = -10;
	int d = c >> 1;
	printf("%d\n", c);
	printf("%d\n",d);
	return 0;
}

运行结果:

其实不难发现,算术右移其实也有一种÷2的效果。

(3)右移图示解析

总结:大多数编译器都是使用算术右移,效果相当于÷2

算术右移口诀:右边丢弃,左边补原符号位

3.左移、右移总结

(1)移动的对象只能是整数

(2)只能移动正整数位,比如左移一位、右移两位。

(3)移动后不会改变原数据的值

(4)移动的是二进制的补码,打印的结果是二进制的原码

三、位操作符

前言:位操作符的对象仍然是二进制的补码,他们必须有两个操作对象

1.按位与(&)

(1)定义

两个对象的补码比较,相同位有0则结果位0,相同位都是1结果才为1。

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 5;
	int b = 7;
	int c = a & b;//将按位与的结果赋值给c

	printf("%d\n",c);
	return 0;
}

运行结果:

(3)代码分析

我们先列出5和7的补码:

5的补码:00000000 00000000 00000000 00000101

7的补码:00000000 00000000 00000000 00000111

图解:

(4)实列讲解

根据有0为0,两个1才为1的特点,可以按位与1来确定最低一位(最右一边)是1或者0。

代码演示:

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 3;
	int b = a & 1;
	printf("%d\n",b);
	return 0;
}

运行结果:

结果分析:

3的补码:00000000 00000000 00000000 00000011

1的补码:00000000 00000000 00000000 00000001

所以,任何数按位与1,得出的结果为1,则该数的二进制最低为是1,如果是0,则最低为是0。当然,要知道该数的二进制序列的所以1,则需要配合右移符号使用(后续补充)

2.按位或(|)

(1)定义

二进制序列对应位上,有1则为1,两个相同才为0。

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 5;
	int b = -3;
	int c = a | b;//按位或
	printf("%d\n",c);
	return 0;
}

运行结果:

(3)代码解读

5的补码:00000000 00000000 00000000 00000101

-3的补码:11111111 11111111 11111111 11111101

3.按位异或(^)

(1)定义

对应的二进制位相同为0,相异为1

由此而来的知识点:a^a=0 a^0=a

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 5;
	int b = -3;
	int c = a ^ b;//按位异或
	printf("%d\n", c);
	return 0;
}

运行结果:

(3)代码解读

5的补码:00000000 00000000 00000000 00000101

-3的补码:11111111 11111111 11111111 11111101

(4)实例讲解

题目:在不借助第三方变量的情况下,交换两个变量的值

代码演示:

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 520;
	int b = 1314;
	a = a ^ b;
	b = a ^ b;//此时的a=a^b
	a = a ^ b;
	printf("a=%d b=%d\n",a,b);
	return 0;
}

运行结果:

代码解读:

第一步:a=a^b;这个结果先不要算出来

第二步:b=a^b;这个时候左边的a=a^b,所以整个等式变成b=a^b^b,因为b^b=0,且a^0=a,所以此时就将a的值赋给了b,

第三步:a=a^b,此时左边的a=a^b,b=a,所以左边=a^b^a=b,所以就将b赋值给了a,完成两个数的交换

四、某些复合赋值操作符和一个单目操作符

前言:操作对象都是二进制的补码

1.复合赋值操作符

(1)赋值操作符的介绍
>>= 右移等
<<= 左移等
&= 按位与等
|= 按位与等
^= 按位异或等

(2)使用方法

上面的这些符号就多了一个等号,就如同+=一样

代码举例:

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 5;
	a = a >> 1;//普通写法
	a >>= 1;//复合写法

	a = a << 1;
	a <<= 1;

	a = a & 2;
	a &= 2;//复合写法

	a = a | 4;
	a |= 4;

	a = a ^ 1;
	a ^= 1;
	return 0;
}

2.一个单目操作符

(1)定义:~(一个波浪号)

作用:对一个数的二进制按位取反

(2)代码演示

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 0;
	printf("%d\n",~a);//单目操作符
	return 0;
}

运行结果:

(3)例子

cpp 复制代码
#include<stdio.h>
int main()
{
	int a = 10;
	a |= (1 << 4);//控制二进制将10变成26
	printf("%d\n",a);
	//再将26变回10
	a &= (~(1 << 4));
	printf("%d\n",a);
	return 0;
}

10的补码:00000000 00000000 00000000 00001010

26的补码:00000000 00000000 00000000 00011010

10变成26:

26变回10:只需将该位置按位与上0即可


相关推荐
远望清一色2 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself12 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
凌云行者21 分钟前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
XiaoLeisj23 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
凌云行者25 分钟前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
杜杜的man27 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*28 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家28 分钟前
go语言中package详解
开发语言·golang·xcode
llllinuuu29 分钟前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s30 分钟前
Golang--协程和管道
开发语言·后端·golang