TCP, UDP 서버 공부 #1 Nodejs / C로 TCP, UDP 서버 만들어 보기
C/C++ 공부하기 #2 비트 연산자, 비트 이동(Shift) 연산자
2019-01-15
Explanation
지난 글에 이어서 이번 글은 비트 연산자와 비트 이동(시프트) 연산자 입니다.
& 연산자는 두개의 비트가 모두 1일 때 1을 반환하는 연산입니다.
예를 들어 0 – 0 이면 0이고 0 – 1 이거나 1 – 0 이어도 0 입니다.
1-1. 예제
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main() { int num1 = 4; // 00000000 00000000 00000000 00000100 int num2 = 8; // 00000000 00000000 00000000 00001000 int num3 = num1 & num2; printf("%d", num3); return 0; } // 0 |
지난 글에서 적었듯 정수는 4바이트로 표현합니다.
그리고 4와 8은 같은 위치에서 1인 비트가 없기 때문에 출력하는 값은 0입니다.
1-2. 예제
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main() { int num1 = 1; // 00000000 00000000 00000000 00000001 int num2 = 3; // 00000000 00000000 00000000 00000011 int num3 = num1 & num2; printf("%d", num3); return 0; } // 1 |
둘의 & 연산 결과는 00000000 00000000 00000000 00000001 이기 때문에 출력값은 1입니다.
| 연산자는 두개의 비트중 하나라도 1이라면 1을 반환합니다.
예를 들어 0 – 0 이면 0이고 0 – 1 이거나 1 – 0, 1 – 1 이면 1 입니다.
2-1. 예제
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main() { int num1 = 4; // 00000000 00000000 00000000 00000100 int num2 = 1; // 00000000 00000000 00000000 00000001 int num3 = num1 | num2; printf("%d", num3); return 0; } // 5 |
| 연산 결과는 00000000 00000000 00000000 00000101 이기 때문에 출력값은 5입니다.
^ 연산자는 두개의 비트가 다를 경우 1를 반환합니다.
예를 들어 0 – 0 이거나 1 – 1 이면 0이고 0 – 1 이거나 1 – 0 이면 1 입니다.
3-1. 예제
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main() { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = 7; // 00000000 00000000 00000000 00000111 int num3 = num1 ^ num2; printf("%d", num3); return 0; } // 8 |
^ 연산 결과는 00000000 00000000 00000000 00001000 이기 때문에 출력값은 8입니다.
~ 연산자는 0에서 1로 1은 0으로 반전시키는 연산으로 보수 연산이라고도 불린다고 합니다.
한가지 추가로 생각하는 부분은 왼쪽 첫번째에 위치한 부호비트(MSB)도 반전 된답니다.
4-1. 예제
1 2 3 4 5 6 7 8 9 10 |
#include <stdio.h> int main() { int num1 = 15; // 00000000 00000000 00000000 00001111 int num2 = ~num1; printf("%d", num2); return 0; } // -16 |
15의 비트의 0은 1로 1은 0으로 바꾸면 11111111 11111111 11111111 11110000 가 되겠죠? 그런데 첫번째 부호비트(MBS)가 1로 음수이기 때문에 이 값은 2의 보수를 취해 알 수 있습니다.
2의 보수를 구하면 00000000 00000000 00000000 00010000 가 나오고 이는 16입니다. 그렇게 하여 11111111 11111111 11111111 11110000는 -16 라는 것을 알 수 있습니다.
5-1. 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> int main() { int num1 = 10; // 00000000 00000000 00000000 00001010 int shift1 = num1 << 1; // 00000000 00000000 00000000 00010100 int shift2 = num1 << 2; // 00000000 00000000 00000000 00101000 int shift3 = num1 << 3; // 00000000 00000000 00000000 01010000 printf("%d\n", shift1); printf("%d\n", shift2); printf("%d", shift3); return 0; } // 20 // 40 // 80 |
위 예제를 보면 알 수 있듯이 << 연산자 뒤에 나오는 숫자만큼 비트가 왼쪽으로 이동하고 가장 오른쪽은 0으로 채워집니다. 그리고 왼쪽으로 1칸 이동할때마다 정수의 값은 2배로 커지고 역으로 오른쪽으로 1칸 이동할때마다 정수의 값은 반이 된다는 것을 알 수 있습니다.
그리고, 기다리고 기다리던 CPU에 관한 이야기.
상황에 따라 곱샘과 나눗샘은 비트의 이동 연산으로 구할 수 있고 이는 성능 향상으로 이어집니다. (CPU 입장에서는 곱셈과 나눗샘이 비트 이동보다 부담스러운 연산이기 때문에)
자바스크립트로 예를 들자면 이렇겠네요.
1 2 |
console.log(4/2); // 2 console.log(4>>1) // 2 : 성능우위 |
6-1. 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> int main() { int num1 = -16; int shift1 = num1 >> 1; int shift2 = num1 >> 2; int shift3 = num1 >> 3; printf("%d\n", shift1); printf("%d\n", shift2); printf("%d", shift3); return 0; } // -8 // -4 // -2 |
위 예제를 보면 알 수 있듯이 >> 연산자 뒤에 나오는 숫자만큼 비트가 오른으로 이동합니다. 그리고 (양수일 때) 가장 왼쪽의 비트는 0으로 채워집니다.
그리고, 여기서 다시 한번 기다리고 기다리던 CPU에 관한 이야기.
책의 이야기에 따르면, 양수 일때는 비트가 오른쪽으로 한칸 이동할때마다 가장 왼쪽 비트에 0이 채워지지만 음수 일때는 CPU에 따라서 왼쪽에 비트를 0으로 채우기도, 1로 채우기도 한다고 합니다.
저의 컴퓨터에서는 출력값이 -8, -4, -2로 부호비트를 유지하는 시스템이네요. 그렇다면 오른쪽 시프트를 하면 왼쪽 비트에 1이 채워진다는 것을 알 수 있습니다.
근데.. 사실 아직 완전히 이해한건 아니에요. 오른쪽 시프트틑 할 때 왼쪽에 0이 채워지는 경우라면, 값의 부호도 바뀌고 값이 많이 달라지게 될 것 같은데, 그러면 앞에서 너무 당연한 듯 정수는 오른쪽 시프트를 한칸 할때마다 값은 2로 나누어진 값이 된다고 했는데. 이 또한 CPU에 따라서 제한적인 사항이겠군요?…
음… 이 부분은 조금 더 검색을 하고 알아봐야 할 거 같아요. 하지만 지금은 시간이 너무 늦어서 다음에 추가하는 것으로…
혹시 이 부분에 대해 잘 아시는 분이 댓글로 설명해주시면 참 감사할 거 같아요 :)