24.3 부호 있는 자료형의 비트 연산 알아보기
지금까지 부호 없는(unsigned) 자료형으로 비트 연산을 했습니다. 하지만 부호 있는 자료형을 비트 연산할 때는 부호 비트를 조심해야 합니다.
먼저 부호 없는 자료형과 부호 있는 자료형에 >> 연산을해보겠습니다.
bitwise_right_shift_signed_unsigned.c
#include <stdio.h> int main() { unsigned char num1 = 131; // 131: 1000 0011 char num2 = -125; // -125: 1000 0011 unsigned char num3; char num4; num3 = num1 >> 5; // num1의 비트 값을 오른쪽으로 5번 이동 num4 = num2 >> 5; // num2의 비트 값을 오른쪽으로 5번 이동 printf("%u\n", num3); // 4: 0000 0100: 맨 뒤의 11은 사라지고 0000 0100이 됨 printf("%d\n", num4); // -4: 1111 1100: 모자라는 공간은 부호 비트의 값인 1로 // 채워지므로 1111 1100이 됨 return 0; }
실행 결과
4 -4
1바이트짜리 부호 없는 변수에 131과 부호 있는 변수에 -125를 할당했습니다. 부호 없는 변수의 1000 0011(131)을 오른쪽으로 5번 이동시켰을 때는 0000 0100(4)가 나왔는데 부호 있는 변수의 1000 0011(-125)를 오른쪽으로 5번 이동시키니 1111 1100(-4)가 나왔습니다. 왜 이렇게 다른 값이 나올까요?
1000 0011(131) _________ >> 5 0000 0100(4)
1000 0011(-125) _________ >> 5 1111 1100(-4)
부호 있는 자료형의 첫 번째 비트는 부호 비트라고 하는데 이 비트가 1이면 음수, 0이면 양수입니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-2.png)
부호 있는 자료형에 저장된 1000 0011은 첫 번째 비트가 1이므로 음수이고 10진수로는 -125가 됩니다. 이 비트들을 오른쪽으로 5번 이동시키면 모자라는 공간은 모두 부호 비트의 값으로 채워지기 때문에 1111 1100(-4)가 됩니다. 하지만 부호 없는 자료형은 비트를 오른쪽으로 이동해도 모자라는 공간은 모두 0으로 채워집니다. 즉, 비트 연산자는 부호 있는 자료형과 부호 없는 자료형이 다르게 동작합니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-3.png)
그러면 부호 있는 자료형에서 부호 비트가 0인 양수에 >> 연산을 하면 어떻게 될까요?
bitwise_right_shift_signed.c
#include <stdio.h> int main() { char num1 = 67; // 67: 0100 0011 char num2; num2 = num1 >> 5; // num1의 비트 값을 오른쪽으로 5번 이동 printf("%d\n", num2); // 2: 0000 0010: 모자라는 공간은 부호 비트의 값인 0으로 채워지므로 // 0000 0010이 됨 return 0; }
실행 결과
2
부호 있는 자료형에 저장된 0100 0011(67)은 부호 비트가 0입니다. 따라서 오른쪽으로 5번 이동했을 때 모자라는 공간은 부호 비트의 값인 0으로 채워지므로 0000 0010(2)이 됩니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-4.png)
이번에는 부호 있는 자료형에서 << 연산을 해보겠습니다.
bitwise_left_shift_signed.c
#include <stdio.h> int main() { char num1 = 113; // 113: 0111 0001 char num2 = -15; // -15: 1111 0001 char num3, num4, num5, num6; num3 = num1 << 2; // num1의 비트 값을 왼쪽으로 2번 이동 num4 = num2 << 2; // num2의 비트 값을 왼쪽으로 2번 이동 num5 = num1 << 4; // num1의 비트 값을 왼쪽으로 4번 이동 num6 = num2 << 4; // num1의 비트 값을 왼쪽으로 4번 이동 printf("%d\n", num3); // -60: 1100 0100: 부호 비트를 덮어쓰게 되므로 양수에서 음수가 됨 printf("%d\n", num4); // -60: 1100 0100: 이미 음수인 수는 계속 음수가 됨 printf("%d\n", num5); // 16: 0001 0000: 이미 양수인 수는 계속 양수가 됨 printf("%d\n", num6); // 16: 0001 0000: 부호 비트를 덮어쓰게 되므로 음수에서 양수가 됨 return 0; }
실행 결과
-60 -60 16 16
부호 있는 자료형에서 첫 번째 비트가 0인 양수 0111 0001(113)을 왼쪽으로 2번 이동시키면 1이 부호 비트를 덮어쓰게 됩니다(오버플로우 상황). 따라서 1100 0100(-60)이 되고, 양수였던 수가 음수가 되어버립니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-5.png)
이미 첫 번째 비트가 1인 음수 1111 0001(-15)는 왼쪽으로 2번 이동시켜서 부호 비트를 덮어쓰더라도 부호 비트는 바뀌지 않으므로 계속 음수 1100 0100(-60)이 됩니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-6.png)
비트를 왼쪽으로 좀 더 이동시켜서 0이 부호 비트를 덮어쓰게 만들면 음수였던 수는 양수가 됩니다. 즉, 음수인 1111 0001(-15)를 왼쪽으로 4번 이동시키면 1은 모두 사라지고, 부호 비트에 0이 와서 양수 0001 0000(16)이 됩니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-7.png)
이미 첫 번째 비트가 0인 양수 0111 0001(113)을 왼쪽으로 4번 이동시키면 앞의 1은 모두 사라지고, 부호 비트에 0이 오므로 계속 양수 0001 0000(16)이 됩니다.
![](https://dojang.io/pluginfile.php/246/mod_page/content/22/unit24-8.png)
부호 있는 자료형에서 비트를 왼쪽으로 이동시켰을 때는 부호 비트에 위치한 숫자에 따라 양수, 음수가 결정됩니다. 따라서 부호 있는 자료형에 시프트 연산을 할 때는 의도치 않은 결과가 나올 수 있으므로 항상 부호 비트를 생각해야 합니다.