深入理解C#位运算:掌握位运算符的妙用

1. 开头一问

本文开始前,先给出站长前几天面试,一位面试官C语言大佬给的一题:

int i = 255;
i <<= 24;
i >>= 24;

问题:

  1. 最终i 等于多少?

  2. 如果iuint类型,最终i结果是多少?

2. C#位运算

C#位运算是一种强大的工具,可以在处理二进制数据和位操作时发挥重要作用。通过使用位运算符,我们可以对整数进行位级别的操作,如位与、位或、位异或和位取反等。位运算可以用于优化性能、压缩数据、实现位掩码和位标志等。了解和掌握C#位运算的基本原理和常见应用场景,将使我们能够更高效地处理二进制数据,并在某些情况下提高代码的性能和可读性。通过深入理解C#位运算,我们可以在编程中发挥更大的创造力和灵活性。

本节内容主要参考文章:C# 中使用位运算(与、或、非 & | ^)进行数据校验[1]和c# 位运算符_c#位运算符-CSDN博客[2]

要学会位运算,首先要清楚什么是位运算?程序中的所有内容在计算机内存中都是以二进制的形式储存的(即:0或1),位运算就是直接对在内存中的二进制数的每位进行运算操作。

在C#中可以对整型运算对象按位进行逻辑运算。按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位。C#支持的位逻辑运算符如表所示。

运算符号意义运算对象类型运算结果类型对象数实例
~位逻辑非运算,按位取反整型,字符型整型1~a
&位逻辑与运算,其实与&&逻辑运算符有一致的地方同上同上2a & b
|位逻辑或运算,同样与||有类似的地方同上同上2a | b
^位逻辑异或运算同上同上2a ^ b
<<位左移运算同上同上2a<<4
>>位右移运算同上同上2a>>2

2.1. ~:位逻辑非运算

位逻辑非运算是单目的,只有一个运算对象。位逻辑非运算按位对运算对象的值进行非运算,即:如果某一位等于0,就将其转变为1;如果某一位等于1,就将其转变为0。

比如,对二进制的10010001进行位逻辑非运算,结果等于01101110,用十进制表示就是:

~145等于110;对二进制的01010101进行位逻辑非运算,结果等于10101010。用十进制表示就是~85等于176。

int a = 1001 0001; // 十进制:145
int b = ~a;   // b = 0110 1110,即十进制:110

来复杂的,看这篇文章c# 位运算符_c#位运算符-CSDN博客:

int a = 13;
int b = -14;

Console.WriteLine(~a); // -14
Console.WriteLine(~b); // 13;

这个是怎么操作二进制的呢,首先要要记住一些原则,**就是正数的反码,补码都是其本身的源码,负数的反码是符号位不变,本身的0变1,1变0,补码就是反码+1,最后进行补码取反时连同符号位一起变得到的反码就是结果:

int b   = 1111 1110; // 前面的1111表示符号位
反码       = 1111 0001; // 符号位不变
补码       = 1111 0010; // 反码加1
补码取反   = 0000 1101; // 得到新的反码就是结果,连同符号位一起反)

int a   = 0000 1101;
源补码    = 0000 1101;
源补码取反  = 1111 0010; // 得到一个新源码(既不是补码,也不是反码),连同符号位一起取反
新源码补码    = 0000 0001; // 新的补码,将新源码减1
新的补码取反 = 1111 1110; // 得到新的反码,就是结果

上面先进行的负数的按位取反操作,首先得到反码,然后负数的补码操作是反码加1,符号位都不变,然后把得到的补码取反(符号位一起取反),得到的反码就是结果,符号位为0,结果得正。

然后进行正数的取反,首先得到正数的补码(源补码,到了这一步就开始了类似与负数取反的操作),把正数的补码进行取反,得到一个新码,既不是反码也不是补码,当做中间值进行操作,对他进行补码,不同的是负数取反是加1,那么正数取反就是减1,再次变更符号位,得到补码,然后将补码取反,得到的反码就是结果。把得到的结果转成十进制要注意,负数的符号位是不参与计算的。

经过多次实验得到一个规律就是:

~(+a)=  -(a+1);(正数按位取反只需要把当前数加1然后改成负的)

~(-a)=   (+a-1);(负数按位取反只需要把当前数先当做正数,然后减1得到结果)

2.2. $:位逻辑与运算

位逻辑与运算将两个运算对象按位进行与运算。与运算的规则:1与1等于1,1与0等于0。

示例代码:

int a = 13;
int b = 14;
int result = a & b; // 12

转换为二进制:

int a = 0000 1101;
  int b = 0000 1110;
int result = 0000 1100;

&运算符会对二进制相同位置上面的0和1进行对比,当相同位置数字相同时就返回这个相同的数,否则就返回0,是不是和&&运算符判断两个bool 一致则返回True,否则返回False类似。所以我们得到了result这个结果,转化成十进制就是12了。

2.3. |:位逻辑或运算

位逻辑或运算将两个运算对象按位进行或运算。或运算的规则是:1或1等1,1或0等于1,

示例代码:

int a = 13;
int b = 14;
int result = a | b; // 15

转换为二进制:

int a = 0000 1101;
  int b = 0000 1110;
int result = 0000 1111;

其实判断方式是一样的,只是返回的结果不一样, | 运算符判断两个二进制相同位置的0和1,只要其中一个位置的数字是1就返回1,是不是和 || 运算符同样很类似,只要一个True则返True,再把得到的结果转化成10进制就是15了。

2.4. ^:位逻辑异或运算

位逻辑异或运算将两个运算对象按位进行异或运算。异或运算的规则是:1异或1等于0,

1异或0等于1,0异或0等于0。即:相同得0,相异得1。

示例代码:

int a = 13;
int b = 14;
int result = a ^ b; // 3

转换为二进制:

int a = 0000 1101;
  int b = 0000 1110;
int result = 0000 0011;

从中可以看出,^ 判断相同位置上面的数字时,如果两个数相同,不论是0还是1都返回0,如果其中一个为1就返回1。而 | 是只要有一个只要位置上一个为1就返回1,所以名字叫异或(不同的返回或)。

2.5. <<:位左移运算

位左移运算将整个数按位左移若干位,左移后空出的部分0。比如:8位的byte型变量byte a=0x65(即二进制的01100101),将其左移3位:a<<3的结果是0x27(即二进制的00101000)。

byte a = 0110 0101;
a <<= 3;//0010 1000

将第一个操作数向左移动第二个操作数指定的位数,空出的位置补0。左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8。

x<<1= x*2 
 x<<2= x*4 
 x<<3= x*8 
 x<<4= x*16

2.6. >>:位右移运算

位右移运算将整个数按位右移若干位,右移后空出的部分填0。比如:8位的byte型变量byte a=0x65(既(二进制的01100101))将其右移3位:a>>3的结果是0x0c(二进制00001100)。

byte a = 0110 0101;
a >>= 3;//0000 1100

将第一个操作数向右移动第二个操作数所指定的位数,空出的位置补0。

右移相当于整除. 右移一位相当于除以2; 右移两位相当于除以4; 右移三位相当于除以8。

x>>1= x/2 
 x>>2= x/4 
 x>>3= x/8 
 x>>4= x/16

3. 总结及问题答案

看微软文档位运算符和移位运算符[3]两点注意

  1. 位运算和移位运算永远不会导致溢出,并且不会在已检查和未检查的[4]上下文中产生相同的结果。

  2. 移位运算符仅针对 intuintlongulong 类型定义,因此运算的结果始终包含至少 32 位。如果左侧操作数是其他整数类型(sbytebyteshortushortchar),则其值将转换为 int 类型。

这里给出文章开头问题的答案及答案解析结束本文:

int i = 255; // 00000000 00000000 00000000 11111111
i <<= 24;  // 11111111 00000000 00000000 00000000
i >>= 24;  // 11111111 11111111 11111111 11111111;

上面移位后是-1,修改i数据类型为uint

uint i = 255; // 00000000 00000000 00000000 11111111
i <<= 24;  // 11111111 00000000 00000000 00000000
i >>= 24;  // 00000000 00000000 00000000 11111111;

无符号类型移位后是原来的数值255,int和uint位移后结果不同的原因:

  1. 对于有符号整数的右移操作,会将最高位的符号位也一同右移。这意味着如果原始数值的最高位是1,那么右移后,符号位会被保留,即填充1。这种右移操作被称为算术右移。

  2. 对于无符号整数的右移操作,不会保留符号位,而是将最高位的0也一同右移。这种右移操作被称为逻辑右移。

本文如果有错误的地方欢迎指正,文中参考资料:

  • https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators

  • c# 位运算符

  • C# 中使用位运算(与、或、非 & | ^)进行数据校验

参考

[1]

C# 中使用位运算(与、或、非 & | ^)进行数据校验: https://www.cnblogs.com/yuyoho/p/13185690.html

[2]

c# 位运算符_c#位运算符-CSDN博客: https://blog.csdn.net/pq8888168/article/details/82116450

[3]

位运算符和移位运算符: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators

[4]

已检查和未检查的: https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/checked-and-unchecked

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值