C의 비트 단위 연산자
C는 비트단위 논리연산자와 시프트 연산자를 제공한다.
비트단위 논리 연산자.
네개의 비트 단위 논리 연산자(char형을 포함한) 정수형 데이터에 적용된다. 이들을 비트 단위 연산자라고 부르는 이유는, 각 비트에 연산이 적용될 때 왼쪽이나 오른쪽에 있는 비트들에 영향을 받지 않기 때문이다.
1의 보수, 비트단위 부정(negation) 연산자 : ~
단항 연산자 ~는, 다음과 같이, 각 비트에 대해 1은 0으로, 0은 1로 바꾼다.
~(10011010) -> (01100101)
unsigned char 형인 val에 값 2가 대입되어 있다고 가정하자. 2진수 표기로 2는 00000010이다. 따라서 ~val은 값 11111101, 즉 253이다.
3 * val 이 val의 값을 변경시키지 않듯이, 이 연산자는 val의 값을 변경시키지 않는다. 그러므로 val은 여전히 2이다. val의 값을 ~val로 바꾸고 싶다면 대입을 사용하라.
val = ~val;
비트 단위 논리곱(AND)연산자 : &
이항 연산자 &는 두 피연산자를 대응하는 비트끼리 서로 비교함으로써 새로운 값을 만든다. 대응하는 두 비트가 모두 1일 때에만 결과 비트가 1이다.
(10010011) & (00111101) 의 값은 (00010001)이다.
C는 비트단위 논리곱 - 대입 결합 연산자 &= 를 제공한다.
val &=0377;
다음과 같은 문장과 동일하다.
val = val & 0377;
비트 단위 논리합(OR) 연산자 : |
이항 연산자 |는 두 피연산자를 대응하는 비트끼리 서로 비교함으로써 새로운 값을 만든다. 각각의 비트 위치에 대해, 피연산자들의 대응하는 두 비트중 어느하나라도 1이면 결과 비트가 1이다.
(10010011)|(00111101)의 결과는 (10111111)이다.
C는 비트 단위 논리합(OR) - 대입 연산자 |=를 제공한다.
val |= 0377;
위 문장은 다음과 같다.
val = val | 0377;
비트단위 배타적 논리합(Exclusive OR)연산자 : ^
이항 연산자 ^는 두 피연산자를 대응하는 비트끼리 서로 비교함으로 써 새로운 값을 만든다. 피연산자들의 대응하는 두 비트중 어느 하나가(둘다가 아니라) 1이면, 결과 비트가 1이다.
(10010011) ^ (00111101)의 결과는 (10101110)이다.
C는 비트단위 배타적 논리합(Exclusive OR)- 대입 결합 연산자 ^=를 제공한다.
val ^= 0377;
위 문장은 다음과 같다.
val = val ^ 0377;
용도 : 마스크
비트 단위 논리곱 연산자는 흔히 마스크(mask)와 함께 사용된다.
일부 비트들은 1, 일부 비트들은 0으로 설정된 비트 패턴을 마스크라 한다.
예를 들어 #define을 사용하여 기호 상수 MASK를 2로 정의 했다고 가정하자. 그럼 2는 2진수로 00000010이다.
그러면 다음과 같은 문장은
flags = flags & MASK;
flags의 하나의 비트를 제외한 모든 비트들을 0으로 설정한다. 그 이유는 & 연산자를 사용하여 0과 결합되는 비트는 결과 비트가 0이 되기 때문이다. 그러나 MASK의 1이 있는 위치의 flags 비트는 변하지 않는다.
위 논리를 확장하면, 마스크에 있는 0인 비트들을 불투명막으로, 1인 비트들은 투명막으로 생각할 수 있다. 표현식 flags & MASK는 flags의 비트패턴을 마스크로 가리는 것과 같다. 즉, MASK에 있는 1인 비트들에 대응하는 비트들만 볼 수 있다.
C에서 마스크의 가장 일반적인 용도는 다음과 같은 문장이다. ch & 0xff;
값 0xff는 2진수로 11111111이기 때문에, 이 마스크는 ch의 마지막 8비트만 남기고 모두 0으로 설정한다. 따라서 ch가 8비트, 16비트 또는 그 이상이라도 상관없이, 최종값을 1바이트에 넣을 수 있는 값으로 만들어 준다.
용도 : 비트 켜기(On)(비트 설정)
어떤 경우 특정 비트들만 켜고, 나머지 비트들을 변경시키지 말아야할 경우가 있다. 이러한 경우에 비트 단위 논리합 | 연산자를 사용하면 된다.
예를 들어, 비트 1번이 1로 설정된 (00000010) MASK를 가지고 있다고 가정하자.
flags = flags | MASK;
flags의 비트 1번이 1로 설정되고, 나머지는 변경되지 않는다.
ex) flags는 00001111이고, MASK는 10110110이다.
flags |= MASK;
(00001111) | (10110110)
위 값은
(10111111)이다.
MASK에서 1로 정해진 모든 비트들은 결과적으로 1로 정해진다. MASK에서 0비트에 상응하는 flags에 있는 모든 비트들은 변경되지 않는다.
용도 : 비트 끄기(OFF)(비트 삭제)
어떤 경우 특정 비트들만 끄고, 나머지 비트들을 변경시키지 않는 것이 유용한 경우도 있다.
flags의 비트 1번만 끄기를 원한다고 가정하자. MASK는 비트 1번만 1로 설정되어 있다. 그러면 다음과 같은 문장을 사용할 수 있다.
flags = flags &~MASK;
~MASK는 비트 1번을 제외한 나머지 비트들이 모두 1이다. 11111101 이다. 또한 &를 사용하여 ~MASK의 1인 비트에 결합하는 비트는 변경되지 않고 0인 비트와 결합하는 비트는 0이 된다. 그러므로 원래의 값과 무관하게 0으로 설정된다.
flags가 00001111이고 MASK가 10110110이라고 가정하면 flags &= ~MASK;는
(00001111) &~(10110110)-> (00001111) &(01001001) -> (00001001)이 된다.
결과적으로 MASK에서 1로 설정된 모든 비트들은 0으로 설정된다. MASK에 0비트에 상응하는 flag에서의 모든 비트들은 변경되지 않고 남는다. flags&= ~MASK;
용도 : 비트 토글
비트를 토글(toggle)한다는 것은, on을 off로, off를 on으로 전환하는 것을 의미한다. 비트를 토글하기 위해서는 배타적 논리합(Exclusive OR) 연산자 ^를 사용할 수 있다. b가 비트세팅(1 또는 0)일때, b가 1이면 1^b는 0이다. b가 0이면 1^b 는 1이다. 또한 0 ^ b의 값이 무엇이든 간에 그대로 b가 된다. 그러므로 ^연산자를 사용하여 어떤 값을 마스크와 결합하면, 마스크에 있는 1인 비트에 대응하는 값들은 토글되고, 0인 비트에 대응하는 값들은 변하지 않는다.
예를 들면 flags가 00001111이고 MASK가 10110110이라고 가정하면 flags ^ MASK는 (00001111) ^ (10110110)이 된다. 이값은 (10111001)이 된다.
MASK에서 1로 설정된 모든 비트들이 토글되고 있는 flags의 비트에 결과적으로 상응하게 된다. MASK에 있는 0비트에 상응하는 flags 비트는 변경되지 않는다.
용도 : 비트 값 검사
특정 비트 하나의 값만 검사하고 싶다. flag에 있는 비트 1번이 1인지 검사하는 방법은 다음과 같이 할 수 있다.
if((flag & MASK) == MASK)
puts("wow!");
위와 같은 방법을 사용한다면 이것은 특정 위치에 있는 비트를 검사하는 것이 아니라 모든 비트들의 세팅을 비교하는 것이다. 그로므로 비트 1번만 비교하려면 MASK를 사용하여 flag의 비트 1번을 제외한 모든 비트들을 마스크 처리 해야한다.
MASK = (00000010)
if((flag & MASK) == MASK)와 같은 과정을 통해 특정 비트의 값을 알 수 있다.
비트 연산자는 == 보다 우선순위가 낮다. 그러므로 flag&MASK를 괄호로 둘러싸야 한다.
마스크(MASK)의 비트 수가 마스크하려는 값(flag)의 비트수와 같거나 커야한다.