언어/C언어

[C언어] 비트연산자 NOT, OR, AND, XOR, SHIFT, 2의보수법

REAKWON 2019. 1. 27. 10:23

비트연산자


컴퓨터가 사용하는 모든 데이터들은 전부 1과 0으로 이루어진 비트열이라는 것을 다들 잘 알겁니다.


C언어에서도 역시 그렇습니다. 우리는 정수형 변수 a에 16이라는 데이터를 집어넣는 것은 사실 코딩할때만 그렇습니다. 하지만 실행이 될때는 이진수로 메모리에 저장이 되어있죠.


만약

int a=16

이라고 정수를 메모리에 넣어준다면 이렇게 메모리에 잡히게 됩니다.

int는 4바이트의 메모리를 갖고 있으므로 

0000 0000 0000 0000 0000 0000 0001 0000

이렇게 저장이 되지요.


우리는 이러한 비트로 연산을 수행할 수 있습니다. 오늘은 이런 비트 연산에 대해서 알아보도록 하겠습니다.


NOT ( ~ )

NOT연산자는 비트열을 반전시키는 연산자입니다. 직관적으로 이해하기도 쉽습니다.

만약 비트가 1이면 0으로 반전하고, 0이면 1로 바꾸기만 하면 되니까요. 연산자 기호는 ~를 씁니다.


그래서 만약

~1000 0110 는 0111 1001로 바뀌게 됩니다.


 

OR( | )

OR 연산자는 | 입니다. 엔터 위쪽 \가 보이시나요? 이것을 쉬프트기로 눌러 입력한게 바로 OR연산자 | 입니다. A OR B는 A 또는 B가 1이라면 답은 1이 되는 겁니다.


0 | 0 = 0

0 | 1 = 1

1 | 0 = 1

1 | 1 = 1


두 비트가 모두 0일때 0이라는 것을 알 수 있네요.


다음의 계산 예를 봅시다. 


      0111 1001

 OR 1000 1010

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

      1111 1011



AND( & )

AND 연산자는 두 비트열 모두 1일때 1이 됩니다. 


0 & 0 = 0

0 & 1 = 0

1 & 0 = 0

1 & 1 = 1


둘 다 1일때만 1인것을 알 수 있습니다.


다음의 예를 보고 AND연산에 대해서 보도록 합시다.


        0001 1001

AND  0111 1000

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

        0001 1000


OR연산과 AND 연산은 정말 쉽습니다.


XOR( ^ )

XOR 연산자는 두 비트열 중 1이 하나만 있을때 답이 1이 됩니다. 또는 1이 홀수개 일때 답이 1이 된다고 기억하면 됩니다.


0 ^ 0 = 0

0 ^ 1 = 1

1 ^ 0 = 1

1 ^ 1 = 0


1이 홀수개일때만 1이 됩니다.



       1001 1010

XOR 0110 1111

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

       1111 0101



SHIFT( <<, >>)

쉬프트 연산은 비트를 옮기는 연산을 수행합니다. 옮기는 방향에 따라 두 종류가 있습니다. 바로 left shift와 right shift가 그것이죠.

비트를 왼쪽으로 옮기려면 << 연산을 사용하고, 오른쪽으로 옮기려면 >>연산을 사용합니다.




옮길 기준이 되는 비트열은 항상 연산자의 왼쪽, 얼만큼 옮길 건지를 결정하는 건 연산자의 오른쪽에 위치합니다.


만약 Left Shift 연산으로 왼쪽으로 비트열을 옮긴다면 가장 오른쪽에서부터 옮긴 비트열 길이까지 0으로 채워집니다.


하지만 Right Shift 연산으로 비트열을 오른쪽으로 옮기면 가장 상위비트(MSB:Most Significant Bit 라고 합니다.)를 왼쪽에서부터 채웁니다.



아래 예를 보면서 이해합시다.


0011 0110 << 3 = 1011 0000


왼쪽으로 3비트를 이동시키니 001은 삭제되었습니다. 그리고 10110이라는 비트열이 왼쪽으로 3비트 이동이 되지요. 그렇다면 자리가 3개가 남겠군요. 그건 0으로 채웁니다. 이것을 패딩(padding)이라고 하지요.


0011 1100 >> 3 = 0000 0111


이제 오른쪽 비트이동입니다. 우선 이 비트를 오른쪽으로 3비트를 옮기니 100이 삭제가 됩니다. 그후 나머지 4비트는 오른쪽으로 3비트를 이동하면 왼쪽에 3비트를 추가로 채워야하지요. 옮기기전 가장 상위비트(MSB)는 빨간색으로 표시된 0입니다. 그러니 오른쪽으로 이동할때는 이 MSB를 가지고 왼쪽비트를 채우니 000으로 채워지는 겁니다.


1001 1011 >> 4 = 1111 1001


자, 이제 MSB는 1입니다. 이 비트를 오른쪽으로 4비트를 이동하니 1001은 없어지겠죠? 그후 나머지 4비트를 오른쪽으로 이동하고 비워진 4비트는 MSB로 채우니 1로 채워지는 것입니다.


아! 여기서 2의 보수법을 모르시는 분을 위해서 잠시 간략하게 설명하겠습니다.


자료형에는 음수를 나타낼 수 있는 signed 타입와 unsigned 타입이 있습니다. 우리가 int, char와 같이 그냥 사용하게 된다면 default로 signed 자료형입니다. 하지만 음수를 표현할 필요가 없다면 unsigned라는 키워드를 붙여 unsigned int와 같이 표현합니다.

이제 음수를 나타낼 수 있는 signed 자료형에 대해서 음수를 표현하는 방법을 설명하겠습니다.


signed자료형에서 컴퓨터는 MSB에 따라 음수인지 양수인지 구별하는데요.

0이면 양수, 1이면 음수를 나타냅니다.

만약 위의 예에서 0011 1100은 MSB가 0이므로 양수입니다. 

그러니 정수로 표현하면 MSB를 제외하고 32+16+8+4로 70이 됩니다.


하지만 위의 예처럼 1111 1001은 MSB가 1이므로 음수입니다. 이때 2의 보수를 사용하는데요. 방법은 이렇습니다.

1) MSB를 제외하고 비트를 반전시킨다(이것이 1의 보수법입니다.)

2) 1을 더해준다. (1을 더해주는 것이 2의 보수법입니다.)


1)+2)의 방법에 따라 1111 1001의 음수값을 구해보면

   1000 0110 (1111 1001의 반전)

+ 0000 0001 (1을 더해주는 2의 보수)

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

   1000 0111


이렇게 7이 나오게 되지요. 이때 MSB가 여전히 1이므로 -7이 되는 것이죠.





다음의 코드는 비트 연산에 대한 해답을 제공하는 프로그램입니다. 


#include <stdio.h>
#define SIZE 8

void printBitArray(char bits) {
	
	int i;
	for (i = SIZE-1; i >= 0;i--) {
		char bit = ((1 << i)&bits) > 0 ? '1' : '0';
		printf("%c ", bit);
	}
	printf("\n");
}
int main() {

	char bits1=0b10110110;
	char bits2=0b01101101;
	printf("bits1:"); printBitArray(bits1);
	printf("bits2:"); printBitArray(bits2);
	printf("\n\n");

	printf("NOT bits1:");  printBitArray(~bits1);
	printf("bits1 | bits2 :");  printBitArray(bits1 | bits2);
	printf("bits1 & bits2 :");  printBitArray(bits1 & bits2);
	printf("bits1 ^ bits2 :");  printBitArray(bits1 ^ bits2);
	
	printf("bits1 << 4 :");  printBitArray(bits1 << 4);
	printf("bits2 >> 2 :");  printBitArray(bits2 >> 2);
	printf("bits1 >> 3 :"); printBitArray(bits1 >> 3);
	
}

bits1과 bits2를 바꿔서 실행해보고 printBitArray의 매개변수로 비트연산을 해서 답을 확인해 보세요.


여기까지 비트연산자에 대해서 알아보았습니다.

반응형