Bootstrap

C语言中的移位操作符与原码、补码、反码

前言

在深入理解C语言的移位操作符之前,先让我们熟悉一下整数在计算机中的几种表示方法:原码、补码和反码。

这些表示方法对于理解移位操作符在不同情况下的行为至关重要,尤其是在底层编程系统级开发中,理解整数的内部表示以及如何使用移位操作符是非常重要的技能。

本文将详细探讨整数的三种基本表示方法——原码、补码和反码,以及C语言中的移位操作符的运用。

一、原码、补码、反码

1.基本概念:

  1. 原码:这是最直观的表示方法,正数的原码即其二进制表示,负数的原码在其最高位(符号位)置1,其余位保持不变。

  2. 反码:正数的反码与其原码相同。负数的反码是将其原码除符号位外的所有位取反(0变1,1变0)。

  3. 补码:正数的补码也与其原码相同。负数的补码是其反码加1。补码是计算机内部处理整数的主要方式,因为它简化了加减运算。

原码、反码和补码是计算机中用于表示带符号整数的三种编码方式,它们之间有着紧密的关系相互转换的规则。

2.示例部分

1.假设我们使用8位整数来表示以下数值:

  1. 正数5的表示

    • 原码00000101
    • 补码00000101
    • 反码00000101 (由于是正数,反码与原码相同)
  2. 负数-5的表示

    • 原码10000101 (注:在实际中,原码不常用,这里仅作演示)
    • 反码11111010 (将10000101的非符号位取反)
    • 补码11111011 (反码再加1)

2.分布解析

原码

原码是指将一个整数按照其绝对值大小转换成的二进制数。对于正数,符号位为0;对于负数,符号位为1。例如:

  • 正数5的原码是 0000 0001
  • 负数-5的原码是 1000 0001

反码

反码是将原码除符号位以外的所有位取反(即0变1,1变0)。例如:

  • 正数5的反码是 0000 0001
  • 负数-5的反码是 1111 1110

补码

补码是在反码的基础上,符号位不变,其他各位取反后末尾加1得到的。例如:

  • 正数5的补码是 0000 0001
  • 负数-5的补码是 1111 1111

3.转换关系总结(任意一种记法均可

  • 从原码到反码:对于负数,除了符号位,所有位都取反。

  • 从反码到补码:对于负数,反码基础上加1即可得到补码。

或者

  1. 正数
    • 原码、反码和补码完全相同。
  2. 负数
    • 原码到反码:符号位不变,数值位按位取反。
    • 原码到补码:符号位不变,数值位按位取反,然后末尾加1。
    • 反码到原码:符号位不变,数值位按位取反。
    • 补码到原码:先减去1,再按位取反。

4.具体例子参考

假设我们有一个负数-127:

  • 其原码是 1000 0001
  • 其反码是 1111 1110
  • 其补码是 1111 1111
//整数的2进制表示形式,其实有3种
//原码
//反码
//补码
//内存中存储的起始是:补码的二进制
//所以在参与移位的时候,移动后都是补码
//
//12 - 数值
//2进制:1100
//8进制:14
//10进制:12
//16进制:c
//
//int main()
//{
//	//按照一个数的正负,直接写出它的二进制表示形式得到的就是原码
//	//
//	//正数
//	//正数的原码、反码、补码是相同的
//	//负数的原码、反码、补码要经过计算的
//	//反码是原码的符号位不变,其他位按位取反,就是反码
//	//补码是反码+1
//	//整型占4个字节(32bit)
//	//00000000000000000000000000001010 - 原码
//	//00000000000000000000000000001010 - 反码
//	//00000000000000000000000000001010 - 补码
//	int a = 10;
//
//	//10000000000000000000000000001010 - 原码
//	//11111111111111111111111111110101 - 反码
//	//11111111111111111111111111110110 - 补码
//
//	//11111111111111111111111111110110 - 补码
//	//10000000000000000000000000001001
//	//10000000000000000000000000001010 - 原码 
//
//	int b = -10;
//
//	return 0;
//}
//

5.应用场景

  • 原码:常用于计算机内部的运算和数据传输。
  • 反码:常用于进行减法运算,因为两个数相减可以转化为加上一个数的相反数,这时就需要使用反码。
  • 补码:是最常用的表示方法,因为它可以避免发生数值溢出,并且可以将加法和减法统一处理。

通过以上介绍,我们可以清晰地理解原码、反码和补码之间的关系及其转换方法。这些知识在计算机科学和工程领域中非常重要,特别是在理解和实现计算机中的数值运算时。

6.如何在不同计算机架构中实现原码、反码和补码的转换?(了解即可)

在不同计算机架构中实现原码、反码和补码的转换,可以参考以下步骤:

  1. 原码转反码

    • 正数的反码和原码相同。
    • 负数的反码是将原码中的数值位取反。
  2. 原码转补码

    • 正数的补码和原码相同。
    • 负数的补码是在反码的基础上,将最低位加1。
  3. 反码转原码

    • 反码符号位不变,数值位按位取反。
  4. 反码转补码

    • 正数的补码和反码相同。
    • 负数的补码是在反码的基础上,将最低位加1。
  5. 补码转原码

    • 补码符号位不变,数值位按位取反,末位再加1。
  6. 补码转反码

    • 补码符号位不变,数值位按位取反。

具体例子:

  • 原码+3的二进制表示为00000011。
  • 其反码也是00000011(因为+3是正数)
  • 其补码也是00000011(因为+3是正数)

  • 负数-3的原码为10000011
  • 其反码为11111100
  • 其补码为11111101(因为-3的补码在反码的基础上加了1)

通过这些步骤,可以在不同的计算机架构中实现原码、反码和补码之间的相互转换。

二、C语言中的移位操作符

1.基本概念

在计算机科学中,移位操作符是用于处理二进制数据的一种高效方式。

C语言提供了两种移位操作符:左移 << 和右移 >> 。它们分别用于将二进制位向左或向右移动指定的位数。

在C语言中,我们主要使用补码来存储和操作整数。因此,移位操作实际上是在补码上进行的。

  1. 左移操作符 <<:无论正数还是负数,左移操作都会在右侧填充0,左侧位数被丢弃。

  2. 右移操作符 >>:分为逻辑右移和算术右移。

  • 逻辑右移:(无符号右移)在左侧填充0

  • 算术右移:(有符号右移)会根据补码的符号位填充,保持原有符号不变。

2.移位操作符示例

  1. 左移操作符 << 示例

     

    假设我们有一个整数a = 5,在8位系统中表示为00000101

    • int b = a << 1;a左移一位,得到b的二进制为00001010,即十进制的10。

    • int c = a << 3;a左移三位,得到c的二进制为01010000,即十进制的80。

      1int a = 10; // 二进制: 0000...01010
      2int b = a << 2; // 移动后b的二进制为: 0000...10100 (等于40)
  2. 右移操作符 >> 示例

    • 算术右移int d = a >> 1;a右移一位,d的二进制为00000010,即十进制的2。

    • 算术右移(负数):如果a = -5,其补码为11111011int e = a >> 2;a右移两位,e的二进制为11111101,仍然是负数,约等于-2。

      1int a = -10; // 二进制: 1111...10110 (补码)
      2int b = a >> 2; // 移动后b的二进制为: 1111...11101 (仍然是负数)
      1unsigned int a = 10; // 二进制: 0000...01010
      2unsigned int b = a >> 2; // 移动后b的二进制为: 0000...00010

3.原码、补码、反码与移位操作的关系

  • 左移操作不会改变数的符号属性,无论是正数还是负数的补码在左移后都会保持原有的符号。

  • 右移操作的符号属性取决于右移的方式。算术右移会保持原有的符号位,而逻辑右移(通常应用于无符号数)会在左侧填充0。

4.注意事项

  • 在使用移位操作符时,不要对负数进行右移,因为C语言标准对此行为没有明确的规定,不同的编译器可能有不同的实现。
  • 移位操作符的位数通常由另一个变量提供,但这个变量应该在合理的范围内,否则结果可能不正确或不可预测。
  • 移位操作在许多场景下非常有用,例如在处理二进制数据、网络协议编码解码、快速乘除、端口号或协议类型等场合。(在解析网络数据包时,使用移位操作符可以快速提取数据包中的特定字段)
  • 在处理位图时,移位操作结合按位与、按位或和按位异或可以高效地读写像素信息。

5、实际应用

  • 快速乘法和除法: 左移相当于乘以2的幂次方,右移相当于除以2的幂次方。
  • 位字段: 在结构体中,可以使用移位操作符来访问和修改特定的位。
  • 掩码操作: 结合按位与&、按位或|和按位异或^,移位操作可以帮助创建和应用掩码。

五、总结

理解原码、补码和反码的概念,以及熟练掌握C语言中的移位操作符,对于任何想要深入底层编程的开发者来说都是必不可少的技能。

原码、补码、反码是理解整数在计算机中如何存储的基础,而移位操作符则是高效处理这些二进制数据的关键。

通过本文的学习,你应该能够更好地理解整数在计算机中的表示方式,以及如何有效地使用移位操作符进行数据处理。掌握这些基础知识,将帮助你在未来的项目中写出更高效、更精炼、更可靠的代码。

请注意,尽管本文提供了解释和示例,但在实际编程中,还应考虑到各种边界条件和潜在的陷阱,以确保代码的健壮性和兼容性。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;