본문으로 바로가기

[C언어] 기본연산자 #4 (비트)

category C 2014. 5. 3. 23:28

비트연산자

비트연산자는 논리연산자와 비슷하나

비트수준에서 논리연산을 한다고 생각하면 된다.

입문자들에게는 분명 어려운 부분에 해당되니

비트연산자에 이러한 부분이 있구나 정도만 이해하면 된다.

 

bit NOT     ~  

bit AND     &

bit OR       |

bit XOR     ^

bit LShift  <<

bit RShift  >>

 

bit

AND

&

OR

|

XOR

^

1

1

1

1

0

1

0

0

1

1

0

1

0

1

1

0

0

0

0

0

 

 

bit NOT ~ (비트 반전)

0000 0000 (부호없는 10진수:  0) -> 1111 1111 (부호없는 10진수:  255)

0101 0101 (부호없는 10진수: 85) -> 1010 1010 (부호없는 10진수:170) 

 

0000 0000 (부호없는 10진수:  0) -> 1111 1111 (부호없는 10진수:  -1)

0101 0101 (부호없는 10진수: 85) -> 1010 1010 (부호없는 10진수:-86) 

 

예)

#include <stdio.h>
void main()
{
 unsigned char a = 0;
 printf("%d\n", a);
 a = ~a;
 printf("%d\n", a);
 a = ~a;
 printf("%d\n", a);
}

 

bit AND &

0000 0000  & 1111 1111 결과: 0000 0000

0101 0101  & 0000 1111 결과: 0000 0101

 

예)

#include <stdio.h>
void main()
{
 char a = 15;               // 0000 1111
 char b = 121;             // 0111 1001
 printf("%d\n", a & b); // 0000 1001
}

 

bit OR |

0000 0000  & 1111 1111 결과: 1111 1111

0101 0101  & 0000 1111 결과: 0101 1111

 

예)

#include <stdio.h>
void main()
{
 char a = 15;              // 0000 1111
 char b = 121;            // 0111 1001
 printf("%d\n", a | b); // 0111 1111
}

 

<~, |, & 를 이용한 실무 활용 예제>

Window API 나 네트워크에서 보내는 데이터 양을 줄여주기 위해 이러한 기법들이 늘리 쓰인다.

 

#include <stdio.h>
#include <conio.h>
#define ROOM1   1  // 0000 0001
#define ROOM2   2  // 0000 0010
#define ROOM3   4  // 0000 0100 
#define ROOM4   8  // 0000 1000

void main()
{
 char cKey;
 char cRoom = 0;
 puts("1,2,3,4 번키를 이용하여 Room1~4 Light ON/OFF 상태로 만듭니다.");

 for (;;)
 {
  cKey = getch();
  switch (cKey)
  {
  case '1':
   if (cRoom & ROOM1)
    cRoom = cRoom & ~ROOM1;
   else
    cRoom = cRoom | ROOM1;
   break;
  case '2':
   if (cRoom & ROOM2)
    cRoom = cRoom & ~ROOM2;
   else
    cRoom = cRoom | ROOM2;
   break;
  case '3':
   if (cRoom & ROOM3)
    cRoom = cRoom & ~ROOM3;
   else
    cRoom = cRoom | ROOM3;
   break;
  case '4':
   if (cRoom & ROOM4)
    cRoom = cRoom & ~ROOM4;
   else
    cRoom = cRoom | ROOM4;
   break;
  default:
   continue;
  }

  if (cRoom & ROOM1)
   printf("Room1( ON) ");
  else
   printf("Room1(OFF) ");
  if (cRoom & ROOM2)
   printf("Room2( ON) ");
  else
   printf("Room2(OFF) ");
  if (cRoom & ROOM3)
   printf("Room3( ON) ");
  else
   printf("Room3(OFF) ");
  if (cRoom & ROOM4)
   puts("Room4( ON)");
  else
   puts("Room4(OFF)");

 }
}

 

bit XOR ^

1100 1010 ^ 1111 1111 결과: 0011 0101

1100 1010 ^ 0000 0000 결과: 1100 1010

대응되는 두 비트가 같으면 0 다르면 1 이다.

 

사실 XOR 의 규칙은 외우기 힘들다. 신의 주신 최고의 선물 중 하나인 '망각'.

슬픈기억도 지워지는 것 처럼 망각이란 때론 우리에게 도움도 되기도 하지만...

그럼 이제는 XOR 의 규칙을 장기기억 장치에 저장시켜보자!

 

옛날 어셈블리를 다룰때는

mov ax, 0 // ax = 0;

라는 표현은 아래와 같은 표현으로 바꾸어 썼다.

xor ax, ax

그 이유는 ax 를 0으로 초기화 시킬때 mov보다 xor를 사용하는 것이 더 빠르기 때문에 xor를 이용해서 0으로 초기화를 한다. 여기서 우리가 생각해야 할 것은 xor의 규칙이다. xor ax, ax 라는 표현은 레지스터에 저장된 ax 값에 상관 없이 똑같은 ax 에 대해 xor 연산을 하게 되므로, 대응되는 두 비트들이 언제나 똑같으며 결과 값은 0 이 된다.

예를 들자면

ax:  0101 1100

ax:  0101 1100

--------------

xor  0000 0000

xor ax, ax '표현이 0이 된다는 사실만 기억'한다면 대응되는 두 비트가 같으면 0가 되며, 두 비트가 틀리면 1이 된다는 규칙을 추론할 수 있다.

 

<XOR 를 이용한 간단한 암호화 활용 예제>

암호학에 대해서는 C언어의 주제를 벗어나나, 키는 비밀키(대칭키)와 공개키로 구분 될 수 있으며, 비밀키는 암호화 할 때 사용되는 키와 복호화 할 때 사용되는 키가 같다 정도만 알고 있으면 된다.

XOR 를 사용하면 간단한 암호화 프로그램을 만들 수 있다. 아래의 예제는 이런 용도로 사용되는 구나!라고 알고 있으면 된다.

 

#include <stdio.h>
#include <string.h>
void main()
{
 char cPrivateKey = 0x59; // Bin 01011001 // Dec 89
 char szBuffer[1024];
 printf("원본 문장을 입력하세요!\n");
 scanf("%s", szBuffer);
 int iLen = strlen(szBuffer);
 for (int i = 0; i < iLen; ++i)
 {
  szBuffer[i] = szBuffer[i] ^ cPrivateKey;
 }
 printf("암호화된 문장\n%s\n", szBuffer);
 for (int i = 0; i < iLen; ++i)
 {
  szBuffer[i] = szBuffer[i] ^ cPrivateKey;
 }
 printf("복호화된 문장\n%s\n", szBuffer);
}

 

bit LShift  <<

비트를 각각 왼쪽으로 이동시키며, 빈공간에 대해서는 0으로 채우게 된다.

1111 1111 << 2  결과 1111 1100

1111 0101 << 4  결과 0101 0000

예)

int a = 1;      // 0001

a = a << 1;  // 0010  // a = a * 2;를 해주는 효과와 같다.

정리: a << n ==  a * (2^n)

 

<질문>

a = a * 2;  와 a = a << 1 중 어떤 표현이 더 좋을까?

-----------------------------------------------------

 int a = 1;
00411A1E  mov         dword ptr [a],1
 a = a * 2;
00411A25  mov         eax,dword ptr [a]
00411A28  shl         eax,1
00411A2A  mov         dword ptr [a],eax

-----------------------------------------------------

 int a = 1;
00411A1E  mov         dword ptr [a],1
 a = a << 1;
00411A25  mov         eax,dword ptr [a]
00411A28  shl         eax,1
00411A2A  mov         dword ptr [a],eax

-----------------------------------------------------

  

bit RShift  >>

비트를 각각 오른쪽으로 이동시키며, 빈공간에 대해서는 0으로 채우게 된다.

1111 1111 >> 2  결과 0011 1111

1010 0101 >> 4  결과 0000 1010

예)

int a = 2;      // 0010

a = a >> 1;  // 0001  // a = a / 2;를 해주는 효과와 같다.

정리: a >> n ==  a / (2^n)

 

<질문>

a = a / 2;  와 a = a >> 1 중 어떤 표현이 더 좋을까?

-----------------------------------------------------

 int a = 100;
00411A1E  mov         dword ptr [a],64h
 a = a >> 1;
00411A25  mov         eax,dword ptr [a]
00411A28  sar         eax,1
00411A2A  mov         dword ptr [a],eax

-----------------------------------------------------

 int a = 100;
00411A1E  mov         dword ptr [a],64h
 a = a / 2;
00411A25  mov         eax,dword ptr [a]
00411A28  cdq             
00411A29  sub         eax,edx
00411A2B  sar         eax,1
00411A2D  mov         dword ptr [a],eax

-----------------------------------------------------