비트 연산자
유닉스에서의 권한 코드(Read, Write, Execute) 같이 하나의 값으로 다중 플래그의 켜짐과 꺼짐을 표현할 수 있습니다. 이 글에서는 C에서의 내용을 바탕으로 Java에서의 활용 방법을 설명합니다.
비트 연산자에는 시프트 연산자를 제외하면 &(and), |(or), ^(xor), ~(비트 반전)의 4개 연산자가 존재합니다. 각각에 대해 살펴보면,
& 연산 : 양쪽 모두 1일 경우에만 1이라는 결과가 나옵니다.
| 연산 : 양쪽 모두 0일 경우에만 0이라는 결과가 나옵니다.
^ 연산 : |(or)와 비슷하지만 양쪽 모두 1일 경우에만 0이라는 결과가 나옵니다.
~ 연산 : 모든 비트를 반전시킵니다. 즉, 1의 보수를 의미합니다.
여기에서 설명한 연산자는 무부호(Unsigned) 정수에 대해서만 동작하게 됩니다. 때문에 정수 형태의 선언이 아닌 16비트 선언이 사용됩니다. 우선 각각의 플래그를 선언해 보도록 하겠습니다.
view plaincopy to clipboardprint?
public static final int FLAG_A = 0x01;
public static final int FLAG_B = 0x02;
public static final int FLAG_C = 0x04;
public static final int FLAG_D = 0x08;
public static final int FLAG_E = 0x10;
public static final int FLAG_F = 0x20;
public static final int FLAG_G = 0x40;
public static final int FLAG_H = 0x80;
public static final int FLAG_A = 0x01;
public static final int FLAG_B = 0x02;
public static final int FLAG_C = 0x04;
public static final int FLAG_D = 0x08;
public static final int FLAG_E = 0x10;
public static final int FLAG_F = 0x20;
public static final int FLAG_G = 0x40;
public static final int FLAG_H = 0x80;
다중 플래그 처리를 위해서는 1, 2, 4, 8과 같은 2진수 형식으로 증가되어야 합니다. 위에서 정의된 각 플래그 상수값을 2진수로 변환해 보면 다음과 같습니다.
view plaincopy to clipboardprint?
0x01 = 0000 0001
0x02 = 0000 0010
0x04 = 0000 0100
0x08 = 0000 1000
0x10 = 0001 0000
0x20 = 0010 0000
0x40 = 0100 0000
0x80 = 1000 0000
0x01 = 0000 0001
0x02 = 0000 0010
0x04 = 0000 0100
0x08 = 0000 1000
0x10 = 0001 0000
0x20 = 0010 0000
0x40 = 0100 0000
0x80 = 1000 0000
위 내용과 같이 각 플래그 상수간의 비트 중복이 없어야 합니다. 비트 연산을 하기 위한 필수 사항입니다. 각각의 상수를 비트 연산 한다고 하면 2진수의 각 자리마다 논리 연산을 수행해 준다는 의미입니다.
FLAG_A와 FLAG_D를 |(or) 연산하면 아래와 같이 "0000 1001"이라는 FLAG_A와 FLAG_D가 동시에 표현된 값이 나옵니다.
view plaincopy to clipboardprint?
0000 0001
0000 1000
---------
0000 1001 // FLAG_A | FLAG_D
0000 0001
0000 1000
---------
0000 1001 // FLAG_A | FLAG_D
위에서 나온 "0000 1001"라는 값에 FLAG_D의 보수(1111 0111)를 &(and) 연산하면 "0000 0001"이라는 FLAG_D를 끈 값이 나옵니다.
view plaincopy to clipboardprint?
0000 1001
1111 0111
---------
0000 0001 // FLAG_A & ~FLAG_D
0000 1001
1111 0111
---------
0000 0001 // FLAG_A & ~FLAG_D
즉 다음과 같이 하나의 값에 복수의 플래그를 켜거나 끌 수 있습니다.
view plaincopy to clipboardprint?
int flag = 0x00; // 플래그 저장 변수
flag |= FLAG_A; // FLAG_A 켜기
flag |= FLAG_D; // FLAG_D 켜기
flag &= ~FLAG_D; // FLAG_D 끄기
int flag = 0x00; // 플래그 저장 변수
flag |= FLAG_A; // FLAG_A 켜기
flag |= FLAG_D; // FLAG_D 켜기
flag &= ~FLAG_D; // FLAG_D 끄기
그리고 아래와 같이 플래그가 켜져 있는지, 혹은 지정한 플래그들 외에 다른 플래그가 켜져있는지 검사하는 논리식이 가능합니다.
view plaincopy to clipboardprint?
if ((flag & FLAG_A) == FLAG_A) ; // FLAG_A 켜져 있음
if ((flag & ~(FLAG_A | FLAG_B)) != 0x00) ; // FLAG_A, FLAG_B 외에 다른 플래그 켜져 있음
if ((flag & FLAG_A) == FLAG_A) ; // FLAG_A 켜져 있음
if ((flag & ~(FLAG_A | FLAG_B)) != 0x00) ; // FLAG_A, FLAG_B 외에 다른 플래그 켜져 있음
각각의 논리식은 2진수 변환 상태에서 &(and), |(or), ~(비트 반전)의 연산을 따라해 보면 확실히 이해할 수 있습니다.