..

Search

54) 비트 단위 연산

54) 비트 단위 연산

비트 단위 연산


비트 단위 연산(bitwise operation)

컴퓨터는 모든 데이터를 비트(bit) 단위로 표현하고 처리합니다.

과거에는 개발자가 직접 비트 단위 연산을 사용하여, 복잡한 연산을 훨씬 더 빠르고 효율적으로 수행해야만 했습니다.

하지만 하드웨어의 발달로 이제는 비트 단위까지 생각하지 않더라도, 충분히 빠른 프로그램을 작성할 수 있게 되었습니다.

 

그러나 아직도 하드웨어 관련 프로그래밍이나 시스템 프로그래밍 등 제한된 자원을 가진 시스템을 위한 프로그램에서는 비트 단위의 연산이 자주 사용되고 있습니다.

이러한 비트 단위 연산을 통해 사용되는 메모리 공간을 줄이거나, 성능의 향상을 기대할 수 있기 때문입니다.


비트 연산자

비트 연산자는 비트(bit) 단위로 논리 연산을 할 때 사용하는 연산자입니다.

또한, 왼쪽이나 오른쪽으로 전체 비트를 이동시킬 때에도 사용합니다.

비트 연산자 설명
~ 비트를 1이면 0으로, 0이면 1로 반전시킴. (비트 NOT 연산)
& 대응되는 비트가 모두 1이면 1을 반환함. (비트 AND 연산)
| 대응되는 비트 중에서 하나라도 1이면 1을 반환함. (비트 OR 연산)
^ 대응되는 비트가 서로 다르면 1을 반환함. (비트 XOR 연산)
<< 지정한 수만큼 비트들을 전부 왼쪽으로 이동시킴. (left shift 연산)
>> 지정한 수만큼 비트들을 전부 오른쪽으로 이동시킴. (right shift 연산)

비트 연산자 진리표

비트 연산자 진리표란 각 비트의 값에 따라 얻을 수 있는 가능한 비트값을 표로 나타낸 것을 가리킵니다.

각 비트 연산자에 대한 진리표는 다음과 같습니다.

비트1 비트2 비트1 & 비트2 비트1 | 비트2 비트1 ^ 비트2 ~ 비트1 ~ 비트2
0 0 0 0 0 1 1
0 1 0 1 1 1 0
1 0 0 1 1 0 1
1 1 1 1 0 0 0

비트 NOT 연산자

비트 NOT 연산자는 주어진 비트가 1이면 0으로, 0이면 1로 반전시켜 1의 보수로 만들어 줍니다.

이러한 비트 NOT 연산자는 피연산자가 단 하나뿐인 단항 연산자입니다.

 

예제

 

int x = 7;        // 00000000 00000000 00000000 00000111  

printf("%d", ~x); // 11111111 11111111 11111111 11111000 : -8  

 

코딩연습 ▶

실행 결과

-8

 

위의 예제처럼 비트 NOT 연산은 부호 비트도 반전시키므로, 결괏값이 음수로 변경됩니다.


비트 AND 연산자

비트 AND 연산자는 두 개의 피연산자 비트가 모두 1일 때만 1을 반환합니다.

이러한 비트 AND 연산자는 두 개의 피연산자를 가지는 이항 연산자입니다.

 

예제

int x = 7;           // 00000000 00000000 00000000 00000111

int y = 10;          // 00000000 00000000 00000000 00001010  

printf("%d", x & y); // 00000000 00000000 00000000 00000010 : 2  

코딩연습 ▶

실행 결과

2


비트 OR 연산자

비트 OR 연산자는 두 개의 피연산자 비트 중 하나라도 1일 때는 1을 반환합니다.

이러한 비트 OR 연산자는 두 개의 피연산자를 가지는 이항 연산자입니다.

 

예제

int x = 7;           // 00000000 00000000 00000000 00000111

int y = 10;          // 00000000 00000000 00000000 00001010  

printf("%d", x | y); // 00000000 00000000 00000000 00001111 : 15  

코딩연습 ▶

실행 결과

15


비트 XOR 연산자

XOR 연산이란  배타적 논리합(exclusive OR)이라고도 불리며, 두 개의 피연산자 중 하나만이 1일 때 1을 반환합니다.

이러한 성질을 이용하면 비트 NOT 연산자는 모든 비트를 반전시키지만, 비트 XOR 연산자는 지정한 비트만을 반전시킬 수 있습니다.

즉, 비트가 1로 설정된 비트와 XOR 연산을 한 비트만이 반전되므로 반전될 비트를 직접 지정할 수 있게 됩니다.

이러한 비트 XOR 연산자는 두 개의 피연산자를 가지는 이항 연산자입니다.

예제

int x = 7;           // 00000000 00000000 00000000 00000111

int y = 10;          // 00000000 00000000 00000000 00001010  

printf("%d", x ^ y); // 00000000 00000000 00000000 00001101 : 13  

코딩연습 ▶

실행 결과

13


비트 시프트 연산자

비트 시프트(shift) 연산자는 비트 이동 연산자라고도 하며, 지정한 수만큼 모든 비트를 전부 좌우로 이동시킵니다.

비트 시프트 연산자에는 다음과 같이 두 가지 연산이 존재합니다.

 

1. 왼쪽 시프트 연산자(<<, left shift)

2. 오른쪽 시프트 연산자(>>, right shift)

 

비트 시프트 연산은 아주 간단한 동작을 수행하지만, 매우 넓은 응용 범위를 가지고 있습니다.


왼쪽 시프트 연산자

왼쪽 시프트 연산자(<<, left shift)는 지정한 수만큼 피연산자의 모든 비트를 전부 왼쪽으로 이동시킵니다.

 

왼쪽 시프트 연산자의 문법은 다음과 같습니다.

문법

피연산자<<이동할비트수

 

이렇게 왼쪽으로 모든 비트를 이동시키면, 맨 왼쪽의 비트는 지정된 수만큼 자동으로 버려지게 됩니다.

그리고 왼쪽으로 이동된 수만큼 비게 되는 오른쪽 비트에는 자동으로 0이 채워집니다.

 

이러한 왼쪽 시프트 연산의 특징은 다음 예제를 통해 살펴볼 수 있습니다.

예제

int x = -1;        // 11111111 11111111 11111111 11111111

int y = x<<2;      // 왼쪽으로 2비트만큼 이동시킴.

int z = x<<3;      // 왼쪽으로 3비트만큼 이동시킴.  

 

printf("%d\n", y); // 11111111 11111111 11111111 11111100 : -4

printf("%d", z);   // 11111111 11111111 11111111 11111000 : -8  

코딩연습 ▶

실행 결과

-4

-8

 

위의 예제에서 -1을 2비트만큼 왼쪽으로 이동한 결괏값은 -4가 되며, 3비트만큼 왼쪽으로 이동한 결괏값은 -8이 됩니다.

즉, 왼쪽 시프트 연산으로 1비트씩 모든 비트를 이동시킬 때마다 피연산자의 값은 두 배씩 증가하게 되는 걸 알 수 있습니다.

 

이러한 특징을 이용하여 속도가 다소 느린 산술 곱셈 연산을 왼쪽 시프트 연산으로 대체할 수 있습니다.


오른쪽 시프트 연산자

오른쪽 시프트 연산자(>>, right shift)는 지정한 수만큼 피연산자의 모든 비트를 전부 오른쪽으로 이동시킵니다.

 

오른쪽 시프트 연산자의 문법은 다음과 같습니다.

문법

피연산자>>이동할비트수

 

이렇게 오른쪽으로 모든 비트를 이동시키면 맨 오른쪽의 비트는 지정된 수만큼 자동으로 버려지게 됩니다.

그리고 오른쪽으로 이동된 수만큼 비게 되는 왼쪽 비트에는 자동으로 0이 채워집니다.

 

이때 오른쪽 시프트 연산은 왼쪽 시프트 연산과는 달리 시스템마다 약간의 차이가 발생하게 됩니다.

일부 시스템에서는 최상위 부호 비트(MSB)까지 시프트 연산의 대상이 되기도 하지만, 일부 시스템에서는 최상위 부호 비트는 시프트 연산의 대상에서 제외하기도 하기 때문입니다.

따라서 최상위 부호 비트가 중요한 의미를 가지는 부호있는 정수에 대해서는 가급적 시프트 연산을 하지 않는 것이 좋습니다.

 

다음 예제에서 오른쪽 시프트 연산의 결괏값은 시스템에 따라 차이가 발생합니다.

예제

int x = -8;        // 11111111 11111111 11111111 11111000

int y = x>>2;      // 오른쪽으로 2비트만큼 이동시킴.

int z = x>>3;      // 오른쪽으로 3비트만큼 이동시킴.  

 

printf("%d\n", y); // 11111111 11111111 11111111 11111110 : -2

printf("%d", z);   // 11111111 11111111 11111111 11111111 : -1

코딩연습 ▶

실행 결과

-2

-1

 

위의 예제에서 -8을 2비트만큼 오른쪽으로 이동한 결괏값은 -2가 되며, 3비트만큼 오른쪽으로 이동한 결괏값은 -1이 됩니다.

즉, 오른쪽 시프트 연산으로 1비트씩 모든 비트를 이동시킬 때마다 피연산자의 값은 두 배씩 감소하게 되는 걸 알 수 있습니다.

 

이러한 특징을 이용하여 속도가 다소 느린 산술 나눗셈 연산을 오른쪽 시프트 연산으로 대체할 수 있습니다.

 

현재 대부분의 시스템에서는 최상위 부호 비트(MSB)를 시프트 연산의 대상에서 제외하고 있습니다.

연습문제