비트연산자
비트연산자는 논리연산자와 비슷하나
비트수준에서 논리연산을 한다고 생각하면 된다.
입문자들에게는 분명 어려운 부분에 해당되니
비트연산자에 이러한 부분이 있구나 정도만 이해하면 된다.
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
-----------------------------------------------------