• C语言第十二课(上):操作符详解【算数、移位、位、赋值操作符】


    目录

    前言:

    一、操作符分类:

    二、操作符详解:

            1.算术操作符+、-、*、/、%:

            2.移位操作符>>、<<:

            1.原码、反码与补码:

            2.左移操作符:

            3.右移操作符:

            4.警告⚠:

            3.位操作符&、|、^:

            ①.按位与 &:

            ②.按位或 |:

            ③.按位异或 ^:

            ④.作用场景:

            4.赋值操作符与复合赋值操作符:

            ①.赋值操作符:

            ②.复合赋值操作符:

    三、总结:


    前言:

            我们在前面几篇文章中对井字棋和扫雷两个阶段性练习进行了详细的练习讲解,想必各位小伙伴们对于前面学到的知识也能较为充分的掌握了。从本文开始,我们将继续进行下一部分关于操作符的学习。

    一、操作符分类:

            简单来说,操作符可以分为以下十类:

    算术操作符移位操作符位操作符
    赋值操作符单目操作符关系操作符
    逻辑操作符条件操作符逗号表达式
    下标引用、函数调用和结构成员

            而在接下来的文章中我也将按照这样的分类和顺序,为各位小伙伴们介绍一些有关操作符的知识。 

    二、操作符详解:

            1.算术操作符+、-、*、/、%:

            算术操作符很简单也很常见,是普遍用于我们代码中的各种量的计算的基本运算,其用法也很简单,与我们平日里的算术逻辑相同:

    1. int main()
    2. {
    3. int a = 10;
    4. int b = 4;
    5. int c = 0;
    6. c = a + b;
    7. printf("a + b = %d\n", c);
    8. c = a - b;
    9. printf("a - b = %d\n", c);
    10. c = a * b;
    11. printf("a × b = %d\n", c);
    12. c = a / b;
    13. printf("a ÷ b = %d\n", c);
    14. c = a % b;
    15. printf("a对b求余 = %d\n", c);
    16. return 0;
    17. }

            运行结果简单明了:

            这些操作符的使用很简单,但我们在使用时仍需注意一些细节问题。除' % '操作符外,其他几个操作符均可以作用于整数与浮点数,而使用' % '操作符进行计算的两个操作数必须为整数,返回的是整除之后的余数。对于' / '操作符,如果两个操作数均为整数,则执行整数除法,而两个操作数中任意一个(或两个)为浮点数,则执行浮点数除法

    1. int main()
    2. {
    3. int a = 10;
    4. int b = 3;
    5. double c = 3.0;
    6. printf("a ÷ b = %d\n", a / b);
    7. printf("a ÷ c = %lf\n", a / c);
    8. //用lf进行打印的double与float类型浮点数默认打印至小数点后六位
    9. //在lf前加上"."+打印位数,可以改变为我们希望打印的位数
    10. printf("a ÷ c = %.1lf\n", a / c);
    11. return 0;
    12. }

            运行结果验证:

            2.移位操作符>>、<<:

            在这里为各位小伙伴们进行说明,移位操作符中的“位”,指的是二进制位,且移位操作符的操作数只能是整数

            在最开始的学习中,我们提到过,数据在计算机中的存储,存储的是其对应的二进制补码,而移位操作符所操作的,正是各数据的二进制补码。

            1.原码、反码与补码:

            在我们开始细致了解移位操作符之前,我们首先要了解什么是原码,什么是反码,什么又是补码。

            我们都知道,对于一个数,计算机要使用一定的编码方式进行存储,在计算机中通过使用电势的高低来对数据进行存储,高电平为1,低电平为0,则每一位上均存在两种情况,则在32位的计算机中就总共存在着2^32种不同的组合,于是我们在计算机中便可以根据电势的高低来存储数据的二进制码。即原码、反码、补码是机器存储一个具体数字的编码方式。

            在数据存储时,整个二进制码的首位我们将其定为符号位,0表示正数,1表示负数:

    +1在32位计算机中的表示为:0000 0000 0000 0000 0000 0000 0000 0001

    - 1在32位计算机中的表示为:1000 0000 0000 0000 0000 0000 0000 0001

            这样直接由数据本身转换成的二进制序列即为数据对应的原码

            那么什么是反码呢?正数的反码就是其本身,而负数的反码则是将数据除符号位以外所有位均按位取反后得到的二进制序列:

    +1在32位计算机中的反码为:0000 0000 0000 0000 0000 0000 0000 0001

    - 1在32位计算机中的反码为: 1111 1111  1111 1111  1111 1111  1111  1110

            最后就是数据的补码了,正数的补码仍是其本身,而负数的补码则是在其反码的基础上+1

     +1在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0001

    - 1在32位计算机中的补码为:  1111 1111  1111 1111  1111  1111  1111 1111

            在我们的计算机中,数据在存储时恰恰存储的是其对应的补码。 

            2.左移操作符:

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

            我们举个例子方便进行研究:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. int b = a << 1;
    7. //表示b等于将a向左移动一位
    8. printf("a = %d b = %d\n", a, b);
    9. int c = -1;
    10. int d = c << 1;
    11. //表示d等于将c向左移动一位
    12. printf("c = %d d = %d\n", c, d);
    13. return 0;
    14. }

            其中a的值为1,则其存储在计算机中的补码即为:

    +1在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0001

            b的值为将a左移一位左边抛弃右边补0,即为:

    0(抛弃)    000 0000 0000 0000 0000 0000 0000 001    0(补位)

    0000 0000 0000 0000 0000 0000 0000 0010

            将其重新化为原码后转换成十进制数即为b的值,其值为2。 

            同理,c的值为-1,其存储在计算机中的补码为:

    - 1在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1111

            而d的值为将c左移一位 ,左边抛弃右边补0,即为:

    1(抛弃)    111 1111 1111 1111 1111 1111 1111 111    0(补位)

    1111 1111 1111 1111 1111 1111 1111 1110

            将其重新化为原码后转换成十进制数即为d的值,其值为-2。  

            将上述例子编译运行进行验证

            可以看到结果正确,示例得到验证。 

            3.右移操作符:

            与左移操作有所不同,右移操作分为两种:逻辑移位算数移位。我们平常见到基本都是算术右移。具体的我们在编写代码时,使用的是逻辑右移还是算术右移,取决于我们所使用的编译器,而我们常用的绝大多数编译器均为算术右移。但是各位小伙伴们在对知识进行学习和了解时,还是应当进行尽可能全面的了解和学习。

            对应的,两种移位的规则也有所不同。在逻辑移位中,与左移操作相似,右边丢弃,左边补0;而在常用的算术移位中,右边丢弃,左边补原本的符号位

            ①.逻辑移位:

            以-1为例:

    - 1在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1111

            将其进行逻辑右移操作,右边丢弃左边补0,即: 

    0(补位)    1111 1111 1111 1111 1111 1111 1111 111    1(丢弃)

    即 0111 1111 1111 1111 1111 1111 1111 1111

            ②.算术移位:

            我们一直所使用的是Visual Studio 2022版本,在该编译器中为算术右移,则仍使用-1为例:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = -1;
    6. int b = a >> 1;
    7. //表示b等于将a向右移动一位
    8. printf("a = %d b = %d\n", a, b);
    9. return 0;
    10. }

            其中a的值为-1,则其存储在计算机中的补码即为:

    - 1在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1111

            则b为将a进行算数右移操作,右边丢弃左边补符号位,即:

    1(补符号位)    1111 1111 1111 1111 1111 1111 1111 111    1(丢弃)

    即 1111 1111 1111 1111 1111 1111 1111 1111

            将其重新化为原码后转换成十进制数即为b的值,其值仍为-1

            我们同样将其编译运行来验证最终结果:

            可以看到,通过运行,我们的示例得到了验证。

            4.警告⚠:

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

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 1;
    6. int b = a << -1;
    7. //表示b等于将a向左移动负一位
    8. printf("a = %d b = %d\n", a, b);
    9. return 0;
    10. }

            编译运行后的结果不是我们期望得到的结果:

            3.位操作符&、|、^:

            与移位操作相同,位操作符也是针对二进制位进行操作和计算的,且位操作符的操作数同样只能是整数

            首先我们要知道这三种符号的意义:按位与 & 、按位或 | 与按位异或 ^ 

            ①.按位与 &:

            按位与,即将两操作数进行比较,按照各位上的数进行类“与”计算

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 3;
    6. int b = -5;
    7. int c = a & b;
    8. //表示c等于将a与b进行按位与操作
    9. printf("a = %d b = %d c = %d\n", a, b, c);
    10. return 0;
    11. }

            其中 的二进制补码为:

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

            b 的二进制补码为:

    - 5在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1011

            则 c 的二进制补码为,a 与 b 每一位均进行与判断均1则为1有0则为0

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

    - 5在32位计算机中的补码为: 1111 1111  1111  1111 1111  1111  1111 1011

    则 c 在计算机中存储补码为: 0000 0000 0000 0000 0000 0000 0000 0011

            将其重新化为原码后转换成十进制数即为c的值,其值为3

            编译运行来验证结果:

            ②.按位或 |:

             按位或,即将两操作数进行比较,按照各位上的数进行类“或”计算

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 3;
    6. int b = -5;
    7. int c = a | b;
    8. //表示c等于将a与b进行按位或操作
    9. printf("a = %d b = %d c = %d\n", a, b, c);
    10. return 0;
    11. }

            其中 的二进制补码为:

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

            b 的二进制补码为:

    - 5在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1011

            则 c 的二进制补码为,a 与 b 每一位均进行或判断1则为10则为0

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

    - 5在32位计算机中的补码为: 1111 1111  1111  1111 1111  1111  1111 1011

    则 c 在计算机中存储补码为:  1111 1111  1111  1111 1111  1111  1111 1011

            将其重新化为原码后转换成十进制数即为c的值,其值为-5

            编译运行起来验证结果:

            ③.按位异或 ^:

            按位异或,即将两操作数进行比较,按照各位上的数进行类“异或”计算

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 3;
    6. int b = -5;
    7. int c = a ^ b;
    8. //表示c等于将a与b进行按位异或操作
    9. printf("a = %d b = %d c = %d\n", a, b, c);
    10. return 0;
    11. }

            其中 的二进制补码为:

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

            b 的二进制补码为:

    - 5在32位计算机中的补码为:1111 1111 1111 1111 1111 1111 1111 1011

            则 c 的二进制补码为,a 与 b 每一位均进行异或判断相异为1相同为0

    +3在32位计算机中的补码为:0000 0000 0000 0000 0000 0000 0000 0011

    - 5在32位计算机中的补码为: 1111 1111  1111  1111 1111  1111  1111 1011

    则 c 在计算机中存储补码为:  1111 1111  1111  1111 1111  1111  1111 1000

            将其重新化为原码后转换成十进制数即为c的值,其值为-8

            编译运行验证结果:

            ④.作用场景:

            曾经有这样一道题目,各位小伙伴们一起来思考一下该如何去处理:

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

            这道题目我们该怎样去实现呢?答案是通过连续使用位操作符异或' ^ '来实现:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. int b = 20;
    7. printf("a = %d b = %d\n", a, b);
    8. //连续使用位操作符异或'^'
    9. a = a ^ b;
    10. b = b ^ a;
    11. a = a ^ b;
    12. printf("a = %d b = %d\n", a, b);
    13. return 0;
    14. }

            我们将他编译运行起来看看其结果:

            可以看到,通过连续的使用位操作符,完美的实现了我们的期望功能。

            4.赋值操作符与复合赋值操作符:

            ①.赋值操作符:

            赋值操作符' = '很简单,但也是极其实用的一种操作符,它可以帮助我们将之前赋予的一个我们不满意的值进行修改,即我们可以自己重新进行赋值,且赋值操作符可以连续使用

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. int b = 20;
    7. int c = 0;
    8. int d = 0;
    9. printf("a = %-2d b = %-2d c = %-2d d = %-2d\n", a, b, c, d);
    10. a = c = b + 10;
    11. //赋值操作符可以连续使用
    12. d = a;
    13. printf("a = %-2d b = %-2d c = %-2d d = %-2d\n", a, b, c, d);
    14. return 0;
    15. }

            编译运行:

            ②.复合赋值操作符:

            例如+、-、*、/、%、>>、<<、&、|、^等,这些运算符都可以和赋值运算符组成复合赋值操作符+=-=*=/=%=>>=<<=&=|=^=,且同样有效:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. int main()
    4. {
    5. int a = 10;
    6. int b = 20;
    7. printf("a = %-2d b = %-2d c = %-2d d = %-2d\n", a, b);
    8. a += b;
    9. printf("a = %d\n", a);
    10. a = 10;
    11. a -= b;
    12. printf("a = %d\n", a);
    13. a = 10;
    14. a *= b;
    15. printf("a = %d\n", a);
    16. a = 10;
    17. a /= b;
    18. printf("a = %d\n", a);
    19. a = 10;
    20. a %= b;
    21. printf("a = %d\n", a);
    22. a = 10;
    23. a >>= b;
    24. printf("a = %d\n", a);
    25. a = 10;
    26. a <<= b;
    27. printf("a = %d\n", a);
    28. a = 10;
    29. a &= b;
    30. printf("a = %d\n", a);
    31. a = 10;
    32. a |= b;
    33. printf("a = %d\n", a);
    34. a = 10;
    35. a ^= b;
    36. printf("a = %d\n", a);
    37. return 0;
    38. }

            编译运行:

    三、总结:

            至此,我们今天关于算数、移位、位和赋值操作符的介绍就到此为止了,这些操作符应用极其广泛,是我们编写代码过程中的好帮手,希望各位下伙伴们下去以后多多思考和理解,勤加练习,熟练掌握相关的原理和用法,提高自己的代码编写工作效率。

            希望各位小伙伴们能从今天的介绍中获得一些收获和启发,我也十分荣幸能为各位小伙伴们的学习生活提供一些帮助。奋斗者在汗水汇集的江河里,将事业之舟驶到了理想的彼岸!

            新人初来乍到,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位看官老爷随时私信批评指正!

  • 相关阅读:
    Abp 实现通过手机号注册用户
    java8 Lambda表达式以及Stream 流
    jenkins集成maven环境
    【前端】彻底搞懂HTTP协议
    写给开发人员的实用密码学(二)—— 哈希函数
    【算法题解】2022河南萌新联赛第(四)场:郑州轻工业大学
    [Java] Java 函数式编程
    位图算法经典剖析(未完)
    C语言网络编程
    跨域配置代理 axios 请求封装
  • 原文地址:https://blog.csdn.net/weixin_59392935/article/details/128039473