주제 : 비트 연산 중 시프트 연산
요구사항 : 시프트 연산의 기능에 대한 이해
(1) 버려지는 비트
위 코드에서 a는 char로 선언됐다.
char는 범위가 -128 ~ 127 까지라고 했는데
어떻게 1024가 출력된 것일까?
그 이유는
<< 연산자는 오른쪽으로 밀린 비트가 버려지지 않고 그대로 유지되기 때문이다.
그러니까 (4bits) a = 1; 일때 a << 3; 은
0 | 0 | 0 | 1 | 0 | 0 | 0 |
이런 형태가 된다는 것이다.
이러면 4bits가 7bits가 되었죠?
똑같이
8bits로 char형이었던 것이
10칸 이동하면서 더 큰 자료형이 된 것입니다.
이걸 %d로 출력을 하니 1024가 문제없이 출력이 됐죠.
반면 >> 연산자는 다릅니다.
왼쪽으로 밀린 것은 얄짤없이 버려집니다.
(2) 채워지는 비트
진법에 대한 이해가 충분하다면
이진법에서 자릿수가 하나씩 늘어나거나 줄어든다면
그것은 십진법으로 표현했을 때 2배, 1/2배 된다는 것을 알고 있을 것이다.
(십육진법도 마찬가지로 16배, 1/16배)
그리고 위에서도 확인했다시피 이것은 시프트 연산과 관련이 있다.
시프트 연산으로 << 1이나 >> 1을 해버리면
해당 정수의 값이 2배, 또는 1/2배가 된다.
(1) 예제에서 1 << 30 을 했더니 \(2^{30}\) 이 출력된 것처럼 말이다.
이 코드를 다시 실행해보자.
#include <stdio.h>
int main() {
int a;
a = 16;
printf("%d\n", a >> 3);
}
이걸 그림으로 한번 그려보면
a = 16
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
a >> 3
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
이렇게 a >> 3은 2가 나온다는 것을 볼 수 있다.
또한
보다시피 왼쪽으로 3칸 밀리고 빈 자리는 0으로 채워진다.
아! 시프트 연산으로 빈 공간이 생기면 그 비트는 0으로 채워지는 것이구나!
반은 맞고, 반은 틀리다.
왜냐하면 항상 0으로 채워지는 것이 아니기 때문인데
위 코드에서 a를 -16으로 바꾸고 실행해보자.
#include <stdio.h>
int main() {
int a;
a = -16;
printf("%d\n", a >> 3);
}
수학적으로 생각했을 때 결과는 정상이다. \(-16\times\frac{1}{2^{3}}\) 이니 -2가 맞다.
그런데 꼼꼼히 생각해보면 어딘가 이상한 점이 있다.
이걸 한번 그림으로 그려보면
a = -16
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
a >> 3
0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |
-16 >> 3 을 했더니 빈 공간 3bits가 0으로 채워져 00011110이 되었다.
-16 >> 3을 한다면 반환값은 -2가 나와야하는데, 00011110은 최상위 비트가 0이므로
음수가 아니다.
실제로 변환해보아도 30이라는 엉뚱한 숫자가 나온다.
이것이 바로 밀려난 비트가 항상 0으로 채워질 때
생기는 문제이다.
음수는 부호가 유지되지 않는다는 문제..
그래서 C언어에서는 시프트 연산을 할 때
무조건 0으로 채워지는 것이 아니라
최상위 비트가 1이었다면, 즉 원래 정수가 음수라면
밀려나서 채워지는 비트도 1로 채워지게 된다.
즉, 11110000(2)은 3칸 밀렸을 때 빈 자리가 1로 채워져 11111110(2)이 되죠
-16 >> 3
1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
이것을 변환해보면 -2가 잘 나오는 걸 볼 수 있다.
그러면 0으로 채워지도록 시프트하는 것은 못하는 걸까?
유감스럽게도 C에서는 언어적 차원에서 지원하는 연산자는 없다.
그러나 Java 등 다른 일부 프로그래밍 언어에서는 이를 위해 또 다른 시프트 연산자가 존재한다고 한다.
만약 C에서 그러한 연산을 원한다면 `logical shift in c` 같은 검색어로 찾아보면 나올 것..
끝.
'Programming > C & C++' 카테고리의 다른 글
[C/C++] 6.1강 - C언어에서 형변환 하기(리터럴 접미사) (0) | 2021.06.16 |
---|---|
[C/C++] 6강 - C언어에서 형변환 하기 (형변환 연산자) (0) | 2021.06.16 |
[C/C++] 5.1강 - C의 다양한 연산자(사칙대입연산자,증감연산자,sizeof,비트연산자) (0) | 2021.06.12 |
[C/C++] 5강 - C의 다양한 연산자(사칙연산자,관계연산자,논리연산자) (0) | 2021.06.09 |
[C/C++] 2.1강 - 변수 이름 규칙, 변수 이름 표기법 (카멜,파스칼,스네이크) (0) | 2021.06.09 |