当前位置: 首页 > news >正文

操作符详解(上)

1.操作符的分类

  • 算术操作符:+, -,*,%,/,
  • 移位操作符:<<  >>         移动的是二进制位
  • 位操作符:&,|,^,       也是使用二进制位进行计算
  • 赋值操作符:=,+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=,
  • 单目操作符:!,++,--,&,*,+,-,~,sizeof,
  • 关系操作符:>,>=,<,<=,==,!=,
  • 逻辑操作符:&&,||,
  • 条件操作符:?:
  • 逗号表达式:,
  • 下标引用:[ ]
  • 函数调用:()
  • 结构成员访问:.,->

上述的操作符,我们已经讲过算术操作符,赋值操作符,逻辑操作符,条件操作符和部分的单目操作符,今天继续介绍一部分,操作符中有一些操作符和二进制有关系,我们先铺垫一下二进制的和进制转换的知识。

2.二进制和进制转换

其实我们经常能听到2进制,8进制,16进制这样的讲法,那是什么意思呢?

其实2进制,8进制,16进制是数值的不同表示形式而已。

比如:数值15的各种进制的表示形式:

 15的2进制:111115的8进制:1715的10进制:1515的16进制:F//16进制的数值之前写:0x
//8进制的数值之前写:0

我们重点介绍一下二进制:

首先我们还是得从10进制讲起,10进制是我们生活中经常使用的,我们已经形成了很多尝试:

  • 10进制中满10进1
  • 10进制的数字每一位都是0~9的数字组成

其实2进制也是一样的

  • 2进制中满2进1
  • 2进制的数字每一位都是0~1的数字组成

那么1101就是2进制的数字了。

2.1 2进制转10进制

其实10进制的123表示的值是一百二十三,为什么是这个值呢?其实10进制的每一位都是有权重的,10进制从右向左是个位,十位,百位……,分别每一位的权重是10^0,10^1,10^2...

 2进制和10进制是类似的,只不过2进制的每一位的权重,从右向左是:2^0,2^1,2^2....

如果2进制的1101,该怎么理解呢?

1*2^3+1*2^2+0*2^1+ 1*2^0 = 1*8+1*4+0+1 = 13

2.1.1 10进制转2进制数字 

2.2 2进制转8进制和16进制 

 2.2.1 2进制转8进制

 8进制的数字每一位是0~7的,0~7的数字,各自写成2进制,最多有3个2进制为就足够了,比如7的二进制是111,所以在2进制转8进制的时候,从2进制序列中右边低位开始向左每3个2进制位会换算一个8进制,剩余不够3个2进制位的直接换算。

如:2进制的01101011,换成8进制:0153,0开头的数字,会被当做8进制

2.2.2 2进制转16进制 

16进制的数字每一位是0~9,a~f的,0~9,a~f的数字,各自写成2进制,最多有4个2进制位就足够了,比如f的2进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进制位会换算一个16进制位,剩余不够4个二进制位的直接换算。

如2进制的01101011,换成16进制

3.原码,反码,补码 

整数的2进制表示方法有三种,则原码,反码和补码

有符号整数的三种表示方法均有符号位数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。

符号位都是用0表示正,1表示负。

正整数的原,反,补码都相同

负整数的三种表示方法各不相同

原码:直接将数值位按照正负数的形式翻译成二进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码

补码得到原码也是可以使用:取反,+1的操作。

对于整形来说:数据存放内存中其实存放的是补码。

为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码可以相互转换,其实运算过程是相同的,不需要额外的硬件电路。

 4.移位操作符

<< 左移操作符

>> 右移操作符

注:移位操作符的操作数只能是整数。

移动的是存储在内存中的二进制位(补码)

4.1 左移操作符

移位规则:左边抛弃,右边补0

int main()
{int  num = 10;int n = num << 1;printf("n=%d\n", n);printf("num = %d\n", num);return 0;
}

 10的原码 000000001010  左移之后变成 000000010100  换算成10进制为20

代码结果为

4.2 右移操作符 

移位规则:首先右移运算分两种

1.逻辑右移:左边用0填充,右边丢弃

2.算术右移:左边用该原值的符号位填充,右边丢弃

右移到底采用算术右移还是逻辑右移是取决于编译器的! 通常采用的都是算术右移

注意:对于移位运算符,不要移动负数位,这个是标准未定义的。

例如:

int num = 10;
num >> -1;  // error

5.位操作符:&,|,^,~ 

位操作符有:

    &        //按位与                与&&区分             &&   逻辑与   

    |         //按位或                 与 | |区分              | |    逻辑或 

    ^         //按位异或

    ~        //按位取反

注意: 他们的操作数必须是整数。 

直接上代码:

按位与:& 

int main()
{int a = 6;// 00000000000000000000000000000110   6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001   7的补码int c = a & b;  // a和b的补码的二进制位进行按位与运算// 对应的二进制位 有0则为0 两个同时为1  才为1// 00000000000000000000000000000110   6的补码// 11111111111111111111111111111001   7的补码// 00000000000000000000000000000000   结果为0printf("%d\n", c);return 0;
}

按位或:|  

​​​​​​int main()
{int a = 6;// 00000000000000000000000000000110   6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001   7的补码int c = a | b;  // a和b的补码的二进制位进行按位或运算// 只要有1,就为1,两个同时为0,才为0// 00000000000000000000000000000110   6的补码// 11111111111111111111111111111001   7的补码// 11111111111111111111111111111111   // 10000000000000000000000000000000// 10000000000000000000000000000001   原码 结果为-1printf("%d\n", c);return 0;
}

按位异或:^ 

int main()
{int a = 6;// 00000000000000000000000000000110   6的补码int b = -7;// 10000000000000000000000000000111// 11111111111111111111111111111000// 11111111111111111111111111111001   7的补码int c = a | b;  // a和b的补码的二进制位进行按位异或运算// 对应的二进制位 相同为0 相异为1// 00000000000000000000000000000110   6的补码// 11111111111111111111111111111001   7的补码// 11111111111111111111111111111111   // 10000000000000000000000000000000// 10000000000000000000000000000001   原码 结果为-1printf("%d\n", c);return 0;
}

按位取反 : ^    

注意: 它和按位与 ,按位或,按位异或不同  前面三个都是双目操作符 它是单目操作符

int main()
{int a = 0;printf("%d\n", ~a);// 按二进制位取反// 00000000000000000000000000000000    0的补码// 11111111111111111111111111111111// 10000000000000000000000000000000// 10000000000000000000000000000001    取反之后的原码 结果为-1return 0;
}

一道变态的面试题:

不能创建临时变量(第三个变量 ),实现两个数的交换。

^操作符的特点^对应的二进制位 相同为0 相异为1所以 3 ^ 3 = 0a ^ a = 0那么 0 ^ 3 = 30 ^ a = a    
所以可以推出 3^3^5 = 53^5^3 = 5int main()
{int a = 3;int b = 5;printf("交换前:a=%d b=%d\n", a, b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交换后:a=%d b=%d\n", a, b);return 0;}

代码结果如下:

 

练习1:编写代码实现:求一个整数存储在内存中二进制中1的个数。 

int count_bit_one(unsigned int n)
{int count = 0;while (n){if ((n % 2) == 1)count++;n = n / 2;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

 还有另外一种方法:

int count_bit_one(int n)
{int i = 0;int count = 0;for (i = 0; i < 32; i++){if (((n >> i) & 1) == 1){count++;}}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

不管二进制中有多少个1,都得算32次,所以时间复杂度高。 

进阶版 (二进制中有多少个1,就算多少次):

//进阶版 
//n = n & (n - 1)
/*假设n = 1011 所以n = 1010n   = 1011n-1 = 1010n   = 1010
n-1  = 1001
n    = 1000
n-1  = 0111
n    = 0000
所以我们可以知道n = n & (n - 1)的作用就是去掉二进制位中最低位的1
*/int count_bit_one(int n)
{int i = 0;int count = 0;while (n){n = n & (n - 1);count++;}return count;
}int main()
{int num = 0;scanf("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}

练习2:二进制位置0或者置1

编写代码将13二进制序列的第5位修改为1,然后再改为0

13的2进制序列: 00000000000000000000000000001101

将第5位置为1后:00000000000000000000000000011101

将第5位置为0后:00000000000000000000000000001101

int main()
{int a = 13;
//将13二进制序列的第5位修改为1 
//                  n
//          13的2进制序列: 00000000000000000000000000001101
//              1<<(n-1) : 00000000000000000000000000010000
// a = a | (1 << (n - 1)): 00000000000000000000000000011101int n = 5;a = a | (1 << (n - 1));printf("%d\n", a);
// 再改为0
//          13的2进制序列:00000000000000000000000000011101
//	        ~( 1<<(n-1)) :11111111111111111111111111101111
//	 a & ~(1 << (n - 1)) :00000000000000000000000000001101a &= ~(a << (n - 1));return 0;
}

以上就是对操作符详解(上)的详细介绍 制作不易 喜欢的朋友记得点赞评论收藏哦!!!   

http://www.dtcms.com/a/267218.html

相关文章:

  • 深入解析Redis 7.0中每种数据类型的底层实现
  • 【Qt】QStringLiteral 介绍
  • 2025最新Telegram快读助手:一款智能Telegram链接摘要机器人
  • 深入理解微服务中的服务注册与发现
  • 《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
  • 云原生微服务间的异步消息通信:最终一致性与系统容错的架构实战
  • 供应链管理学习笔记4-供应链网络设计
  • 前端-CSS-day1
  • QT中的网络通信
  • LLM:位置编码详解与实现
  • 深层神经网络:原理与传播机制详解
  • java的注解和反射
  • JVM的位置和JVM的结构体系
  • 交互式剖腹产手术模拟系统开发方案
  • 【openp2p】学习3:【专利分析】一种基于混合网络的自适应切换方法、装 置、设备及介质
  • C# 事件(事件访问器)
  • vue中添加原生右键菜单
  • [特殊字符]全面解锁远程运维新时代:CRaxsRat v7.4 工具实用指南(附推荐资源)
  • Oracle 高级 SQL 查询与函数详解:多表连接、子查询、聚合、分析函数
  • 冒泡和快速排序的区别
  • faster_lio 原理及代码
  • 【Oracle专栏】分区表增加分区
  • WPF学习笔记(25)MVVM框架与项目
  • spring-ai-alibaba 1.0.0.2 学习(十二)——聊天记忆扩展包
  • 深度学习的核心理论与技术
  • 11_架构演进:从单体到云原生的蜕变
  • 炸鸡派例程-ADC
  • RabbitMQ 4.1.1初体验-队列和交换机
  • 【AI论文】WorldVLA:迈向自回归动作世界模型
  • 第二章 简单程序设计