[c#] 비트 연산의 실제 적용 [닫기]

  1. 비트 연산을 무엇에 사용 했습니까?
  2. 왜 그렇게 편리할까요?
  3. 누군가 아주 간단한 튜토리얼을 추천 해 주시겠습니까?



답변

모든 사람이 플래그 사용 사례에 푹 빠져있는 것처럼 보이지만, 이것이 비트 연산자의 유일한 응용 프로그램은 아닙니다 (가장 일반적이지만). 또한 C #은 다른 기술이 거의 사용되지 않을만큼 충분히 높은 수준의 언어이지만 여전히 알아 두어야 할 가치가 있습니다. 내가 생각할 수있는 것은 다음과 같습니다.


<<그리고 >>당신이 정말로 모든 마이크로를 통해 초조해하는 경우 사업자는 빠르게 증식 물론 2의 힘으로, 닷넷 JIT 최적화는 아마 당신을 당신 (그리고뿐만 아니라 다른 언어의 어떤 점잖은 컴파일러)이 작업을 수행하지만, 할 수 있습니다 확실히하기 위해 이것을 쓸 수도 있습니다.

이러한 연산자의 또 다른 일반적인 용도는 두 개의 16 비트 정수를 하나의 32 비트 정수로 채우는 것입니다. 처럼:

int Result = (shortIntA << 16 ) | shortIntB;

이것은 종종 레거시 이유로이 트릭을 사용하는 Win32 함수와 직접 인터페이스하는 경우에 일반적입니다.

물론 이러한 연산자는 숙제 질문에 대한 답변을 제공 할 때와 같이 경험이없는 사용자를 혼동하고 싶을 때 유용합니다. 🙂

그것은 훨씬 더 나은 가독성을 가지고 있기 때문에 실제 코드에서 당신은 훨씬 더 떨어져 대신 곱셈을 사용하여 수 있습니다 불구하고 JIT는 그것을 최적화 shlshr지침 어쨌든 때문에 성능 저하가 없다.


^연산자 (XOR)를 다루는 몇 가지 흥미로운 트릭이 있습니다. 이것은 실제로 다음과 같은 속성으로 인해 매우 강력한 연산자입니다.

  • A^B == B^A
  • A^B^A == B
  • 당신이 알고 있다면 A^B그것은 무엇을 얘기하는 것은 불가능 A하고 B,하지만 당신은 그들 중 하나를 알고 있다면, 당신은 다른 사람을 계산할 수 있습니다.
  • 연산자는 곱셈 / 나눗셈 / 더하기 / 빼기와 같은 오버플로를 겪지 않습니다.

이 연산자를 사용하여 본 몇 가지 트릭 :

중간 변수없이 두 개의 정수 변수 교체 :

A = A^B // A is now XOR of A and B
B = A^B // B is now the original A
A = A^B // A is now the original B

항목 당 하나의 추가 변수 만있는 이중 연결 목록입니다. 이것은 C #에서 거의 사용되지 않지만 모든 바이트가 중요한 임베디드 시스템의 저수준 프로그래밍에 유용 할 수 있습니다.

아이디어는 첫 번째 항목에 대한 포인터를 추적하는 것입니다. 마지막 항목에 대한 포인터; 추적하는 모든 항목에 대해 pointer_to_previous ^ pointer_to_next. 이렇게하면 양쪽 끝에서 목록을 순회 할 수 있지만 오버 헤드는 기존 연결 목록의 절반에 불과합니다. 순회를위한 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();
    }
}


답변

내 응용 프로그램의 보안을 위해 비트 연산자를 사용합니다. Enum 안에 다양한 레벨을 저장하겠습니다.

[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
}


답변

나는 당신이 생각하는 스도쿠를 해결하는 것이 얼마나 실용적인지 모르겠지만 그것이 있다고 가정합시다.

보드를 보여주고 퍼즐을 직접 풀 수 있지만 동작이 합법적인지 확인하는 스도쿠 해결사 또는 단순한 프로그램을 작성하고 싶다고 상상해보십시오.

보드 자체는 아마도 다음과 같은 2 차원 배열로 표현 될 것입니다.

uint [, ] theBoard = new uint[9, 9];

0은 셀이 여전히 비어 있고 [1u, 9u] 범위의 값이 보드의 실제 값임을 의미합니다.

이제 어떤 움직임이 합법적인지 확인하고 싶다고 상상해보십시오. 분명히 몇 개의 루프로 할 수 있지만 비트 마스크를 사용하면 작업을 훨씬 빠르게 할 수 있습니다. 규칙을 준수하는 단순한 프로그램에서는 중요하지 않지만 솔버에서는 가능합니다.

각 행, 각 열 a 및 각 3×3 상자에 이미 삽입 된 숫자에 대한 정보를 저장하는 비트 마스크 배열을 유지할 수 있습니다.

uint [] maskForNumbersSetInRow = new uint[9];

uint [] maskForNumbersSetInCol = new uint[9];

uint [, ] maskForNumbersSetInBox = new uint[3, 3];

숫자 세트에 해당하는 1 비트를 사용하여 숫자에서 비트 패턴으로의 매핑은 매우 간단합니다.

1 -> 00000000 00000000 00000000 00000001
2 -> 00000000 00000000 00000000 00000010
3 -> 00000000 00000000 00000000 00000100
...
9 -> 00000000 00000000 00000001 00000000

C #에서는 다음과 같이 비트 패턴을 계산할 수 있습니다 ( valueis an uint).

uint bitpattern = 1u << (int)(value - 1u);

위의 줄 1u에서 비트 패턴에 해당하는 것은 00000000 00000000 00000000 00000001왼쪽으로 이동합니다 value - 1. 예를 들어 value == 5다음과 같은 경우

00000000 00000000 00000000 00010000

처음에 각 행, 열 및 상자의 마스크는 0입니다. 보드에 숫자를 입력 할 때마다 마스크를 업데이트하므로 새 값에 해당하는 비트가 설정됩니다.

행 3에 값 5를 삽입한다고 가정합니다 (행과 열은 0부터 번호가 매겨 짐). 행 3의 마스크는에 저장됩니다 maskForNumbersSetInRow[3]. 삽입하기 전에 이미 {1, 2, 4, 7, 9}행 3에 숫자가 있다고 가정 해 보겠습니다 . 마스크의 비트 패턴은 maskForNumbersSetInRow[3]다음과 같습니다.

00000000 00000000 00000001 01001011
bits above correspond to:9  7  4 21

목표는이 마스크의 값 5에 해당하는 비트를 설정하는 것입니다. 비트 또는 연산자 ( |)를 사용하여 수행 할 수 있습니다 . 먼저 값 5에 해당하는 비트 패턴을 만듭니다.

uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)

그런 다음을 사용 operator |하여 마스크에 비트를 설정합니다.maskForNumbersSetInRow[3]

maskForNumbersSetInRow[3] = maskForNumbersSetInRow[3] | bitpattern;

또는 더 짧은 형식 사용

maskForNumbersSetInRow[3] |= bitpattern;

00000000 00000000 00000001 01001011
                 |
00000000 00000000 00000000 00010000
                 =
00000000 00000000 00000001 01011011

이제 마스크는 {1, 2, 4, 5, 7, 9}이 행 (3 행)에 값 이 있음을 나타냅니다 .

확인하고자하는 경우 행에 어떤 값이 operator &있으면 해당 비트가 마스크에 설정되어 있는지 확인하는 데 사용할 수 있습니다 . 마스크에 적용된 해당 연산자의 결과와 해당 값에 해당하는 비트 패턴이 0이 아니면 값이 이미 행에있는 것입니다. 결과가 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.

다음은 보드에 새 값을 설정하고 적절한 비트 마스크를 최신 상태로 유지하고 이동이 합법적인지 확인하는 방법입니다.

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);
}


답변

여기에 수십 개의 비트 트위들 링 예제

코드는 C로되어 있지만 C #에 쉽게 적용 할 수 있습니다.


답변

하드웨어와 통신해야하는 경우 어느 시점에서 비트 트위들 링을 사용해야합니다.

픽셀 값의 RGB 값 추출.

너무 많은 것들


답변

  1. 하나의 제한된 크기 변수를 통해 함수에 많은 인수를 전달하는 데 사용할 수 있습니다.
  2. 장점은 낮은 메모리 오버 헤드 또는 낮은 메모리 비용입니다. 따라서 성능이 향상됩니다.
  3. 나는 그 자리에서 튜토리얼을 쓸 수 없지만 거기에 있다고 확신합니다.


답변

그것들은 다른 응용 프로그램의 전체 부하에 사용할 수 있습니다. 여기에 비트 연산을 사용하는 이전에 여기에 게시 한 질문이 있습니다.

Java에서 비트 AND, 비트 포함 OR 질문

다른 예를 보려면 플래그가 지정된 열거를 살펴보십시오.

내 예에서는 비트 연산을 사용하여 이진수의 범위를 -128 … 127에서 0..255로 변경했습니다 (표현식을 부호 있음에서 부호 없음으로 변경).

여기에 MSN 기사->

http://msdn.microsoft.com/en-us/library/6a71f45d%28VS.71%29.aspx

유용합니다.

그리고이 링크 :

http://weblogs.asp.net/alessandro/archive/2007/10/02/bitwise-operators-in-c-or-xor-and-amp-amp-not.aspx

매우 기술적이며 모든 것을 다루고 있습니다.

HTH