일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- DP
- CCW 알고리즘
- Algorithm
- CCW알고리즘
- 위상정렬
- Python
- 다익스트라
- recursion
- 너비우선탐색
- 큐
- BFS
- 에라토스테네스의 체
- 재귀
- BOJ
- ccw
- binary search
- 소수
- 딕셔너리
- 에라토스테네스
- 알고리즘
- 이진 탐색
- 투 포인터
- dijkstra algorithm
- 비트연산
- Bitmasking
- deque
- 백준
- 비트마스킹
- 외적
- Two Pointers
- Today
- Total
꾸꾸리
4-3. 비트 연산자 본문
1-0. 들어가기 전에
비트 단위로 연산을 진행하는 비트 연산자는 주로 하드웨어 관련 프로그래밍에 활용되지만, 그 이외의 영역에서도 사용되어 메모리 공간의 효율성을 높이고 연산의 주를 줄이는 요인이 되기도 한다. 그런데 이 연산자의 활용적 측면을 지금 언급하면 이해하는데 많은 부담이 있으므로, 여기서는 일단 연산자의 기능을 이해하는데 초점을 맞추자.
1-1. 비트 연산자의 종류
연산자 |
연산자의 기능 |
결합방향 |
& |
비트단위로 AND 연산을 한다. |
-> |
| |
비트단위로 OR 연산을 한다. |
-> |
^ |
비트단위로 XOR 연산을 한다. |
-> |
~ |
단항 연산자로서 피연산자의 모든 비트를 반전시킨다. |
<- |
<< |
피연산자의 비트 열을 왼쪽으로 이동시킨다. |
-> |
>> |
피연산자의 비트 열을 오른쪽으로 이동시킨다. |
-> |
위의 표에서 << 연산자와 >> 연산자는 '비트 이동(shift) 연산자'라 해서 비트 연산자와는 성향이 조금 다르다. 하지만 흔히 비트 연산자의 범주에 포함시켜서 이야기하기 때문에 함께 정리했다.
1-2. &연산자 : 비트단위 AND
&연산은 두 개의 비트가 모두 1일 때 1을 반환하는 연산이다.
0 & 0 -> 0을 반환
1 & 0 -> 0을 반환
0 & 1 -> 0을 반환
1 & 1 -> 1을 반환
다음의 예제를 확인해보자.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main(void) { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = 20; // 00000000 00000000 00000000 00010100 int num3 = num1 & num2; printf("AND 연산의 결과 : %d\n", num3); return 0; } | cs |
<실행 결과>
AND 연산의 결과 : 4
int형 변수의 크기는 일반적으로 4바이트이기때문에 4바이트를 기준으로 설명하겠다.
위 예제 7행을 살펴보자.
00000000 00000000 000000000 00001111
&연산 00000000 00000000 000000000 00010100
----------------------------------------------------------------
00000000 00000000 000000000 00000100
위에서 볼 수 있듯이, num1과 num2를 대상으로 &연산을 하게 되면, 같은 위치에 저장된 비트들간 & 연산이 진행되어 정수 4가 반환된다.
1-3. | 연산자 : 비트단위 OR
| 연산은 두 개의 비트 중 하나라도 1이면 1을 반환하는 연산이다.
0 | 0 -> 0을 반환
1 | 0 -> 1을 반환
0 | 1 -> 1을 반환
1 | 1 -> 1을 반환
& 연산자와 마찬가지로 예제를 통해 확인하자.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main(void) { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = 20; // 00000000 00000000 00000000 00010100 int num3 = num1 | num2; printf("OR 연산의 결과 : %d\n", num3); return 0; } | cs |
<실행 결과>
OR 연산의 결과 : 31
위 예제 7행을 살펴보면 다음과 같다.
00000000 00000000 000000000 00001111
|연산 00000000 00000000 000000000 00010100
-----------------------------------------------------------------
00000000 00000000 000000000 00011111
1-4. ^ 연산자 : 비트단위 XOR
^ 연산은 두 개의 비트가 서로 다른 경우에 1을 반환하는 연산이다.
0 ^ 0 -> 0을 반환
1 ^ 0 -> 1을 반환
0 ^ 1 -> 1을 반환
1 ^ 1 -> 0을 반환
마찬가지로 예시를 통해 살펴보자.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main(void) { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = 20; // 00000000 00000000 00000000 00010100 int num3 = num1 ^ num2; printf("XOR 연산의 결과 : %d\n", num3); return 0; } | cs |
<실행 결과>
XOR 연산의 결과 : 27
위 예제 7행을 살펴보면 다음과 같다.
00000000 00000000 000000000 00001111
^연산 00000000 00000000 000000000 00010100
----------------------------------------------------------------
00000000 00000000 000000000 00011011
1-5. ~ 연산자 : 비트단위 NOT
~ 연산은 비트를 0에서 1로, 1에서 0으로 반전시키기 때문에 보수연산이라고도 불린다.
~0 -> 1을 반환
~1 -> 0을 반환
아래 예제를 확인해보자.
1 2 3 4 5 6 7 8 9 | #include <stdio.h> int main(void) { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = ~num1; printf("NOT 연산의 결과 : %d\n", num2); return 0; } | cs |
<실행결과>
NOT 연산의 결과 : -16
위 예제의 6행을 살펴보면 다음과 같다.
11111111 11111111 11111111 11110000
이는 num1에 저장된 데이터의 모든 비트를 반전시킨 결과로, MSB 역시 반전되었다. 따라서 이 값은 음수이다.
[4-2. 정수와 실수의 표현방식] 부분에서 음의 정수 표현방법에 대해 알아봤다.
2의 보수를 이용해 양의 정수를 음의 정수로 바꿨는데, 이를 역으로 이용해서 위 데이터의 크기를 계산해보자.
양의 정수를 이용해 음의 정수를 구할 때 1의 보수를 취하고 1을 더하는 과정(2의 보수)을 거쳤다.
그렇다면, 음의 정수의 크기를 구하기 위해 우리는 이와 반대의 과정을 하면 됨을 알 수 있다.
11111111 11111111 11111111 11110000
------------------①1을 뺀다 --------------------
11111111 11111111 11111111 11101111
------------②1의 보수를 취한다----------------
00000000 00000000 00000000 00010000
이진수 10000 은 +16 이므로 위 예제에서 num2가 -16임을 알 수 있다.
1-6. << 연산자 : 비트의 왼쪽 이동(Shift)
<< 연산자는 두 개의 피연산자를 요구하며 다음의 의미를 갖는다.
* num1 << num2 num1의 비트 열을 num2칸씩 왼쪽으로 이동시킨 결과를 반환
* 8 << 2 정수 8의 비트 열을 2칸씩 왼쪽으로 이동시킨 결과를 반환
아래 예제를 통해 << 연산의 결과를 확인해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> int main(void) { int num = 15; // 00000000 00000000 00000000 00001111 int result1 = num << 1; // num의 비트 열을 왼쪽으로 1칸씩 이동 int result2 = num << 2; // num의 비트 열을 왼쪽으로 2칸씩 이동 int result3 = num << 3; // num의 비트 열을 왼쪽으로 3칸씩 이동 printf("1칸 이동 결과 : %d\n", result1); printf("2칸 이동 결과 : %d\n", result2); printf("3칸 이동 결과 : %d\n", result3); return 0; } | cs |
<실행 결과>
1칸 이동 결과 : 30
2칸 이동 결과 : 60
3칸 이동 결과 : 120
위 예제 7~9행을 살펴보면, 변수 num에 저장된 값의 비트 열을 왼쪽으로 각각 1, 2, 3 칸씩 이동시키고 있다.
5행(변수 num) : 00000000 00000000 00000000 00001111
7행 : 00000000 00000000 00000000 00011110 // 왼쪽으로 1칸 이동 ( 값 : 30 )
8행 : 00000000 00000000 00000000 00111100 // 왼쪽으로 2칸 이동 ( 값 : 60 )
9행 : 00000000 00000000 00000000 01111000 // 왼쪽으로 3칸 이동 ( 값 : 120 )
위에서 볼 수 있듯이 비트의 이동으로 인해 생기는 오른쪽 빈칸은 0으로 채워지고, 이동으로 인해 밀려나는 왼쪽 비트들은 그냥 버려진다. (비록 그 값이 1이여도 버려진다.) 우리는 위 예제를 통해 다음과 같은 사실을 알 수 있다.
"비트의 열을 왼쪽으로 1칸씩 이동시킬 때마다 정수의 값이 두 배가 된다."
그리고 위 내용을 토대로 다음 사실을 알 수 있다.
"비트의 열을 오른쪽으로 1칸씩 이동시킬 때마다 정수의 값이 2로 나누어진다."
이 사실을 기억하면, 상황에 따라 곱셈과 나눗셈 연산은 비트의 이동 연산으로 대체할 수 있으며, 이는 성능의 향상으로 이어진다.
CPU 입장에서는 곱셈과 나눗셈이 비트의 이동보다 부담스러운 연산이기 때문이다.
1-7. >> 연산자 : 비트의 오른쪽 이동(Shift)
>> 연산자와 << 연산자의 가장 큰 차이점은 비트의 열을 이동시키는 방향에 있다. 음수의 경우만 한번 살펴보자.
예를 들어, -16의 경우를 살펴보면, 이를 나타내는 비트 열은 다음과 같다.
11111111 11111111 11111111 11110000 : -16
이것을 >> 연산을 통해 오른쪽으로 세 칸 이동시키면 어떻게 될까?
이는 CPU에 따라 결과가 달라지는데, 음의 값을 유지하기 위해 1을 채우는 CPU도 있고, 음의 값 유지에 상관하지 않고 0을 채우는 CPU도 있다.
따라서 다음과 같은 결과를 얻을 수 있다.
00011111 11111111 11111111 11111110 // 0이 채워진 경우
11111111 11111111 11111111 11111110 // 1이 채워진 경우
'Programming Language > C' 카테고리의 다른 글
4-2. 정수와 실수의 표현방식 (0) | 2018.07.18 |
---|---|
4-1. 컴퓨터가 데이터를 표현하는 방식 (0) | 2018.07.18 |
3-3. scanf와 C언어의 키워드 (0) | 2018.07.18 |
3-2. 변수와 연산자 (연산자) (0) | 2018.07.13 |
3-1. 변수와 연산자 (변수) (0) | 2018.07.13 |