C# 位运算的实际应用 您使用位运算的目的是什么 为什么这么方便 有人能推荐一个非常简单的教程吗

C# 位运算的实际应用 您使用位运算的目的是什么 为什么这么方便 有人能推荐一个非常简单的教程吗,c#,bitwise-operators,C#,Bitwise Operators,我在我的应用程序中使用位运算符来保证安全性。我将在枚举中存储不同的级别: [Flags] public enum SecurityLevel { User = 1, // 0001 SuperUser = 2, // 0010 QuestionAdmin = 4, // 0100 AnswerAdmin = 8 // 1000 } 然后为用户指定其级别: // Set User Permissions to 1010 // // 0010 // | 1000

我在我的应用程序中使用位运算符来保证安全性。我将在枚举中存储不同的级别:

[Flags]
public enum SecurityLevel
{
    User = 1, // 0001
    SuperUser = 2, // 0010
    QuestionAdmin = 4, // 0100
    AnswerAdmin = 8 // 1000
}
然后为用户指定其级别:

// Set User Permissions to 1010
//
//   0010
// | 1000
//   ----
//   1010
User.Permissions = SecurityLevel.SuperUser | SecurityLevel.AnswerAdmin;
然后检查正在执行的操作中的权限:

// Check if the user has the required permission group
//
//   1010
// & 1000
//   ----
//   1000
if( (User.Permissions & SecurityLevel.AnswerAdmin) == SecurityLevel.AnswerAdmin )
{
    // Allowed
}
  • 它们可用于通过一个大小有限的变量将多个参数传递给函数
  • 优点是低内存开销或低内存成本:因此提高了性能
  • 我不能当场写一篇教程,但我肯定他们在那里

  • 它们可以用于所有不同的应用程序,下面是我之前在这里发布的一个问题,它使用按位操作:

    对于其他示例,请查看(比如)标记的枚举

    在我的示例中,我使用位操作将二进制数的范围从-128…127更改为0…255(将其表示形式从有符号更改为无符号)

    MSN文章在这里->

    这是有用的

    而且,尽管这种联系:

    技术性很强,涵盖了一切


    HTH

    只要您有一个或多个项目组合选项,则按位通常是一个简单的解决方案


    一些示例包括安全位(等待Justin的示例)、计划天数等。

    我不得不说,最常见的用途之一是修改位字段以压缩数据。您通常会在试图节约数据包的程序中看到这一点


    例如,我在C#中使用它们最频繁的一件事就是生成哈希代码。有一些相当好的散列方法使用它们。例如,对于X和Y都是整数的坐标类,我可以使用:

    public override int GetHashCode()
    {
      return x ^ ((y << 16) | y >> 16);
    }
    
    当你需要它们时,它们是有用的,但这并不常见


    最后,您还可以使用它们对数学进行微观优化,如二进制排序。当实现使用除法运算符而不是位移位运算符时,会出现一些问题。这导致在集合的大小达到10000000以上后,BS失败,尽管每个人似乎都被flags用例所吸引,但这并不是位运算符的唯一应用(尽管可能是最常见的)。另外,C#是一种级别足够高的语言,其他技术可能很少使用,但仍然值得了解。以下是我能想到的:


    运算符可以快速乘以2的幂。当然,.NETJIT优化器可能会为您(以及其他语言的任何优秀编译器)完成这项工作,但是如果您真的为每一微秒而烦恼,那么您只需编写此代码即可

    这些运算符的另一个常用用法是将两个16位整数填充到一个32位整数中。比如:

    int Result = (shortIntA << 16 ) | shortIntB;
    
    双链接列表,每个项目只有一个额外变量。这在C#中几乎没有用处,但对于每个字节都计数的嵌入式系统的低级编程来说,它可能会派上用场

    其思想是跟踪第一项的指针;最后一项的指针;对于每个项目,您都要跟踪
    指向上一个项目的指针^指向下一个项目的指针
    。通过这种方式,您可以从任意一端遍历列表,但开销仅为传统链表的一半。下面是遍历的C++代码:

    ItemStruct *CurrentItem = FirstItem, *PreviousItem=NULL;
    while (  CurrentItem != NULL )
    {
        // Work with CurrentItem->Data
    
        ItemStruct *NextItem = CurrentItem->XorPointers ^ PreviousItem;
        PreviousItem = CurrentItem;
        CurrentItem = NextItem;
    }
    
    要从末尾遍历,只需将第一行从
    FirstItem
    更改为
    LastItem
    。这是另一个节省内存的方法

    我在C#中定期使用
    ^
    操作符的另一个地方是,我必须为我的类型计算HashCode,它是一个复合类型。比如:

    class Person
    {
        string FirstName;
        string LastName;
        int Age;
    
        public int override GetHashCode()
        {
            return (FirstName == null ? 0 : FirstName.GetHashCode()) ^
                (LastName == null ? 0 : LastName.GetHashCode()) ^
                Age.GetHashCode();
        }
    }
    

    如果您需要与硬件进行通信,您需要在某个时候使用位旋转

    提取像素值的RGB值


    我不知道解决你认为的数独有多实际,但我们假设是这样。

    想象一下,你想写一个数独解算器,甚至只是一个简单的程序,向你展示棋盘,让你自己解这个谜,但要确保这些动作是合法的

    电路板本身很可能由二维数组表示,如:

    uint [, ] theBoard = new uint[9, 9];
    
    0
    表示单元仍然为空,且[1u,9u]范围内的值为电路板中的实际值

    现在想象一下,你想检查某个动作是否合法。显然,您可以通过几个循环来完成,但位掩码允许您使事情变得更快。在一个只确保遵守规则的简单程序中,这并不重要,但在解算器中它可以做到

    您可以维护位掩码数组,该数组存储关于已插入每行、每列a和每个3x3框中的数字的信息

    uint [] maskForNumbersSetInRow = new uint[9];
    
    uint [] maskForNumbersSetInCol = new uint[9];
    
    uint [, ] maskForNumbersSetInBox = new uint[3, 3];
    
    从数字到位模式的映射非常简单,其中一位对应于该数字集

    1 -> 00000000 00000000 00000000 00000001
    2 -> 00000000 00000000 00000000 00000010
    3 -> 00000000 00000000 00000000 00000100
    ...
    9 -> 00000000 00000000 00000001 00000000
    
    在C#中,可以通过以下方式计算位模式(
    value
    是一个
    uint
    ):

    目标是设置与此掩码中的值5对应的位。您可以使用位or运算符(
    |
    )来完成此操作。首先创建一个与值5对应的位模式

    uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)
    
    或者使用较短的形式

    maskForNumbersSetInRow[3] |= bitpattern;
    
    00000000 00000000 00000001 01001011
                     |
    00000000 00000000 00000000 00010000
                     =
    00000000 00000000 00000001 01011011
    
    现在,您的掩码指示此行(第3行)中有值
    {1,2,4,5,7,9}

    如果要检查,如果行中有某个值,可以使用
    运算符&
    检查掩码中是否设置了相应的位。如果该运算符应用于掩码和对应于该值的位模式的结果为非零,则该值已在该行中。如果结果为0,则该值不在该行中

    例如,如果要检查行中是否有值3,可以通过以下方式进行检查:

    uint bitpattern = 1u << 2; // 1u << (int)(value - 1u)
    bool value3IsInRow = ((maskForNumbersSetInRow[3] & bitpattern) != 0);
    
    00000000 00000000 00000001 01001011 // the mask
                     |
    00000000 00000000 00000000 00000100 // bitpattern for the value 3
                     =
    00000000 00000000 00000000 00000000 // the result is 0. value 3 is not in the row.
    

    uint bitpattern=1u您将出于各种原因使用它们:

    • 储存(及
      00000000 00000000 00000001 01001011
      bits above correspond to:9  7  4 21
      
      uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)
      
      maskForNumbersSetInRow[3] = maskForNumbersSetInRow[3] | bitpattern;
      
      maskForNumbersSetInRow[3] |= bitpattern;
      
      00000000 00000000 00000001 01001011
                       |
      00000000 00000000 00000000 00010000
                       =
      00000000 00000000 00000001 01011011
      
      uint bitpattern = 1u << 2; // 1u << (int)(value - 1u)
      bool value3IsInRow = ((maskForNumbersSetInRow[3] & bitpattern) != 0);
      
      00000000 00000000 00000001 01001011 // the mask
                       |
      00000000 00000000 00000000 00000100 // bitpattern for the value 3
                       =
      00000000 00000000 00000000 00000000 // the result is 0. value 3 is not in the row.
      
      public void insertNewValue(int row, int col, uint value)
      {
      
          if(!isMoveLegal(row, col, value))
              throw ...
      
          theBoard[row, col] = value;
      
          uint bitpattern = 1u << (int)(value - 1u);
      
          maskForNumbersSetInRow[row] |= bitpattern;
      
          maskForNumbersSetInCol[col] |= bitpattern;
      
          int boxRowNumber = row / 3;
          int boxColNumber = col / 3;
      
          maskForNumbersSetInBox[boxRowNumber, boxColNumber] |= bitpattern;
      
      }
      
      public bool isMoveLegal(int row, int col, uint value)
      {
      
          uint bitpattern = 1u << (int)(value - 1u);
      
          int boxRowNumber = row / 3;
          int boxColNumber = col / 3;
      
          uint combinedMask = maskForNumbersSetInRow[row] | maskForNumbersSetInCol[col]
                              | maskForNumbersSetInBox[boxRowNumber, boxColNumber];
      
          return ((theBoard[row, col] == 0) && ((combinedMask & bitpattern) == 0u);
      }