비트 연산자를 잘 활용하면 알고리즘이나 개발을 할 때 유용합니다. 비트 연산자에 대해서 알아보고 활용할 수 있도록 배워 봅시다.
참고로 논리 연산자와 비트 연산자가 비슷하게 생겨서 헷갈리실 수 있습니다. 관련 글 링크를 올려둘 테니, 비교해 보시는 것도 도움이 되실 것 같습니다.
[C/C++] 논리 연산자
논리 연산자는 조건문과 논리적인 표현을 다룰 때 중요합니다. C/C++에는 논리 연산자가 3가지가 있는데 살펴보도록 하겠습니다. 논리 연산자 논리 AND 연산자 (&&) 논리 AND 연산자는 두 개의 조건
77monkey.tistory.com
비트 연산자
비트 연산자는 비트 단위로 데이터를 연산할 때 사용됩니다. 데이터 타입을 표현할 때에는 바이트 단위이지만, 우리는 그 하위 단위인 비트 단위로 연산을 하려고 합니다. 예를 들면 char는 1바이트로 가장 작은 단위이지만, 그 안에는 8bit가 포함되어 있습니다.
비트 연산자에는 &(AND), |(OR), ^(XOR), ~(NOT), <<(left shift), >>(right shift)가 있습니다.
AND 연산자 (&)
AND 연산자는 두 비트가 1일 때 결과가 1이고 그렇지 않으면 0이 됩니다.
a와 b가 1bit를 표현한다는 가정하에 아래 표로 연산 결과를 확인해 보도록 하겠습니다.
a(1 bit) | b(1 bit) | a & b |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
논리 연산자에서 &&와 유사하지 않나요? 어쩌면 헷갈리실 수도 있을 것 같습니다. 천천히 다음 연산자도 알아보도록 하겠습니다.
OR 연산자 (|)
OR 연산자는 한 비트가 1일 때 결과가 1이고 둘 다 0일 때 0이 됩니다.
a와 b가 1bit를 표현한다는 가정하에 표를 통해서 연산 결과를 알아봅시다.
a(1 bit) | b(1 bit) | a | b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
논리 연산자에서 ||와 유사합니다. |와 || 연산 결과가 유사하다고 같은 것은 아닙니다. 예를 들면 1 | 1 의 결과는 1이지만, 3(0b11) | 5(0x101) 의 결과는 7(0x111) 입니다. 그에 반해 1 || 1의 결과는 1로 OR 연산자와 같지만, 3 || 5 의 결과는 1입니다. 차이점이 보이실까요??
XOR 연산자 (^)
두 비트 중 하나만 1인 경우 결과는 1이고 비트가 같은 값이면 0이 됩니다.
a와 b가 1bit를 표현한다고 했을 때 연산 결과는 아래와 같습니다.
a(1 bit) | b(1 bit) | a ^ b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
AND 연산자도 아니고, OR 연산자도 아닌 게 조금 독특한 연산자입니다.
NOT 연산자 (~)
NOT 연산자는 1을 0으로, 0을 1로 반전시킬 때 사용하는 연산자입니다. 다른 연산자는 이항 연산자로 연산자 앞뒤로 피연산자가 필요하지만, NOT 연산자는 단일 연산자로 연산자 뒤에 피연산자 하나만 필요합니다.
a(1 bit) | ~a |
0 | 1 |
1 | 0 |
주의하실 것은 NOT 연산자(~)와 논리 NOT 연산자(!)와 다릅니다. 8bit 내에서 ~2(0b00000010)은 -3(0b11111101)입니다. 하지만 !2(0b00000010)는 참의 부정이기 때문에 거짓인 0입니다.
left shift 연산자 (<<)
left shift 연산자는 왼쪽으로 n비트만큼 이동합니다. 예를 들면 3(0b11)을 3<<1을 하게 되면 6(0b110)이 됩니다.
왼쪽으로 이동한 만큼 0으로 채워지는데, 0의 개수가 피연산자 개수만큼 늘어난다고 생각하시면 편하실 수도 있을 것 같습니다.
right shift 연산자(>>)
right shift 연산자는 오른쪽으로 n비트만큼 이동합니다. 예를 들면 3(0b11)을 3>>1을 하게 되면 1(0b1)이 됩니다.
오른쪽으로 이동한만큼 해당 비트가 사라지고 오른쪽으로는 0이 채워집니다.
비트 연산자 예시
비트 연산자를 배워봤는데 코드로 살펴보도록 하겠습니다.
#include <stdio.h>
int main() {
int a = 2;
int b = 3;
printf("a & b => 0x%x\n", a & b); // 0b10 & 0b11 => 0b10
printf("a | b => 0x%x\n", a | b); // 0b10 | 0b11 => 0b11
printf("a ^ b => 0x%x\n", a ^ b); // 0b10 ^ 0b11 => 0b01
printf("a << b => 0x%x\n", a << b); // 0b10 << 3 => 0b10000
printf("a >> b => 0x%x\n", a >> b); // 0b10 >> 3 => 0b0
printf("~a => 0x%x\n", ~a); // ~0b10 => 0xfffffffd
return 0;
}
주석으로 bit로 연산을 표현해 보았는데 생각하신 결과와 같으신지요? 실제로 16진수로 출력을 하면 아래와 같이 나옵니다.
a & b => 0x2
a | b => 0x3
a ^ b => 0x1
a << b => 0x10
a >> b => 0x0
~a => 0xfffffffd
마무리
비트연산자는 논리연산자와 비슷하게 생겨서 혼동하실 수도 있습니다. 그렇기 때문에 비트연산자와 논리 연산자를 잘 구별하여 알아두시길 바랍니다. 참고로 비트연산자는 비트연산이기 때문에 비트별로 표현을 하고 연산을 하시면 도움이 됩니다. 해당 값을 10진수로 표현하는 것보다는 16진수로 출력을 하시면 연산의 결과를 확인하실 때 도움이 되실 것입니다.
'개발 > C, C++' 카테고리의 다른 글
[C/C++] 쉼표 연산자(,) (0) | 2023.09.14 |
---|---|
[C/C++] 삼항 조건 연산자(삼항 연산자) (0) | 2023.09.13 |
[C/C++] 논리 연산자 (0) | 2023.09.09 |
[C/C++] 관계 연산자 (0) | 2023.09.08 |
[C/C++] 증감 연산자(++, --) (0) | 2023.09.07 |