혹시, 프로그래밍을 하다가 분명히 unsigned char 타입에 음수를 넣었는 데, signed short이나 int로 형변환하니 이상한 값으로 변하는 경우를 경험하신 적이 있으신가요? 혹은 signed에서 unsigned로 넣었을 경우는 있으신가요?
이미 경험이 있으시다면 그 이유도 아시고, 이후 프로그래밍에서는 조심하려고 하실 것으로 생각합니다. 이 문제는 프로그램이 복잡하다면, 생각보다 찾기 힘들 수도 있기 때문에 코드를 짤 때 주의해야 하는 부분이라고 생각합니다.
그래서 이번 포스팅으로 정리해놓으려고 합니다. 또한, 이게 무슨 문제를 일으킬 지 모르시는 분들을 위해서도 글을 씁니다.
그럼, 바로 설명으로 들어가도록 하겠습니다.
들어가기 전에.. |
포스팅을 위해 작성한 예제에 대해서 간단하게 type을 변경하였으니, 예제를 보실 때 아래를 생각하시고 봐주세요.
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
위처럼, typedef를 이용하여 다음과 같이 설정하였습니다. (signed의 경우에는 prefix 없음, unsigned의 경우에는 u를 붙임)
- char 형일 경우에는 int8 / uint8
- short 형일 경우에는 int16 / uint16
- int 형일 경우에는 int32 / uint32
signed와 unsigned를 간단히 설명하자면 다음과 같습니다. (char 기준)
보통, char의 범위는 -128~127 라는 것을 아실거에요. 이것을 signed char라고 부릅니다.
하지만, unsigned의 경우에는 음수가 없는 char형을 말합니다. 그러므로, 범위도 0 ~ 255 가 되는 것입니다.
자세한 내용은 다른 포스팅으로 정리하도록 하겠습니다.
signed char 형에 음수를 넣었는데, unsigned char 형에서는 양수가 나온다고? |
먼저, 설명하기 전에 생각 한번 해볼까요.
char 형에 -1 이라는 값을 넣었다고 가정해보겠습니다. 이 값을 short나 int형으로 바꾸었을 때, 무슨 값이 출력될까요?
-1이라는 값이 나올까요? 일단, 답은 아래에서 확인해보도록 하겠습니다.
내용을 설명하기 위해서 간단한 예제를 작성하였습니다.
1 int main(void) {
2 int8 num1 = -1;
3 printf("num1 = %d\n", num1); // num1 = -1
4
5 uint8 num2 = num1;
6 printf("num2 = %d\n", num2); // num2 = 255
7
8 int16 num3 = (int16)num2;
9 printf("num3 = %d\n", num3); // num3 = 255
10
11 uint16 num4 = (int16)num2;
12 printf("num4 = %d\n", num4); // num4 = 255
13
14 int8 num5 = (int8)num4;
15 printf("num5 = %d\n", num5); // num5 = -1
16 return 0;
17 }
설명
2번 줄에서 int8(signed char) 타입인 num1 에 -1 값을 넣었습니다. 역시나, 출력해보면 -1이라는 값이 나오는 것을 확인할 수 있습니다.
그럼, 양의 정수만 가지고 있는 uint8(unsigned char) 타입에 int8(signed char) 값을 넣으면 어떻게 될까요?
num2를 출력해보면 255라는 값이 나옵니다. 왜 그럴까요?
이것은 정수 값을 이진수 비트(Bit)로 생각해봐야 합니다.
int8(signed char)에서 -1 값은 이진수로 표현하면 아래와 같습니다.
1 1 1 1 1 1 1 1
이 값은 -1을 1의 보수로 계산한 경우라고 할 수 있습니다. 하지만, unsgined로 표현하면 위의 모든 자릿수를 양의 정수로 생각합니다.
즉, 255라는 값이 나온다는 것입니다.
* 1의 보수에 대한 내용을 아신다면 바로 이해를 하시겠지만, 혹시나 모르신다면 추후 포스팅으로 정리하도록 하겠습니다.
이제, int8(signed char) 타입에 -1 값을 uint8(unsigned char)에 대입하였을 때, 255로 바뀌는 이유를 아시겠나요?
* 그런데, 여기서 문제가 있습니다.
5번 줄부터 9번줄을 보면, uint8(unsigned char)형에서 int16(signed short)형으로 변경해도 255라는 값이 출력됩니다.
다시 signed로 변경하였으면 -1이라는 값이 나와야 할텐데, 왜 255라는 값이 출력될까요?
이제, 그 문제에 대해서 알아보도록 하겠습니다.
unsigned char형을 signed short, int형으로 변경하는데 왜 같은 값이 나올까? |
먼저, 두 가지 정도를 파악하고 설명하도록 하겠습니다.
1. char형과 short, int형을 알아야 합니다.
char형은 8비트로 표현할 수 있는 이진법은 아래와 같습니다.
0 0 0 0 0 0 0 0 ~ 1 1 1 1 1 1 1 1 (총, 8자리 0, 1로 이루어진 숫자)
short형은 16비트로 표현할 수 있는 이진법은 아래와 같습니다.
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ~ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 (총, 16자리 0, 1로 이루어진 숫자)
int형은 32비트로 표현할 수 있는 이진법은 32자리의 0, 1로 이루어진 숫자입니다. (너무 길어서 생략하였습니다.)
2. unsigned -> signed / signed -> unsigned 로 형변환될 때 어떻게 동작하는 지를 알아야 합니다.
- unsigned char 에서 signed short으로 변환하는 경우 ( 값은 -1을 기준으로 하겠습니다.)
unsigned char 에서 -1의 이진수 값은 1이 8자리로 이루어집니다.
1 1 1 1 1 1 1 1 // unsigned char -1을 이진수로 표현
이 값을 signed short으로 변환하면 아래와 같습니다.
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 // signed short -1을 이진수로 표현
unsigned -> signed 변환은 추가되는 자릿 수가 0으로 채워진 다는 것을 알 수 있습니다.
즉, unsigned char의 -1 값을 signed short으로 변환하면 255라는 값이 된다는 것입니다.
- signed char 에서 unsigned short으로 변환하는 경우 ( 값은 -1을 기준으로 하겠습니다.)
signed char 에서 -1의 이진수 값은 1이 8자리로 이루어집니다.
1 1 1 1 1 1 1 1 // signed char -1을 이진수로 표현
이제, 이 값을 unsigned short으로 변환하면 아래와 같습니다.
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 // unsigned short -1을 이진수로 표현
signed -> unsigned 변환은 추가되는 자릿 수가 1으로 채워진 다는 것을 알 수 있습니다.
즉, signed char의 -1 값을 unsigned short으로 변환하면 65535라는 값이 된다는 것입니다.
이것은, 변환되기 전의 타입에 따라 추가되는 자릿 수가 0 또는 1로 채워지는 것입니다.
변환되기 전의 타입이 signed이면, 부호가 있는 것이므로 음수의 숫자일 경우에는 1로 채워지고, 양수의 경우에는 0으로 채워집니다.
unsigned 는 부호가 없으므로, 0으로 채워지는 것입니다.
아래 예제를 보시면 이해하시는데 더욱 편하실 것입니다.
- unsigned char형 -1 값을 short으로 변환하는 경우
uint8 num1 = -1;
printf("num1 = %d\n", num1); // 255
int16 num2 = num1;
printf("num2 = %d\n", num2); // 255
uint16 num3 = num1;
printf("num3 = %d\n", num3); // 255
unsigned char형에서 변환하는 것이므로, 새로 추가되는 자릿 수가 0으로 추가되는 것을 확인할 수 있습니다.
- signed char형 -1 값을 short으로 변환하는 경우
int8 num1 = -1;
printf("num1 = %d\n", num1); // -1
int16 num2 = num1;
printf("num2 = %d\n", num2); // -1
uint16 num3 = num2;
printf("num3 = %d\n", num3); // 65535
signed char형에서 변환하는 것이므로, 새로 추가되는 자릿 수가 1으로 추가되는 것을 확인할 수 있습니다.
- unsigned char형 1 값을 short으로 변환하는 경우
uint8 num1 = 1;
printf("num1 = %d\n", num1); // 1
int16 num2 = num1;
printf("num2 = %d\n", num2); // 1
uint16 num3 = num1;
printf("num3 = %d\n", num3); // 1
unsigned char형에서 변환하는 것이므로, 새로 추가되는 자릿 수가 0으로 추가되는 것을 확인할 수 있습니다.
- signed char형 1 값을 short으로 변환하는 경우
int8 num1 = 1;
printf("num1 = %d\n", num1); // 1
int16 num2 = num1;
printf("num2 = %d\n", num2); // 1
uint16 num3 = num2;
printf("num3 = %d\n", num3); // 1
signed char형에서 변환하지만 부호가 양수이므로, 새로 추가되는 자릿 수가 0으로 추가되는 것을 확인할 수 있습니다.
글을 마치며.. |
결국, 이러한 문제를 해결하기 위해서는 형변환을 할 때, 해당 값이 부호가 없는 타입인 지, 부호가 있는 타입인 지를 확인하여 사용 하는 것이 중요하다고 생각합니다.
프로그램이 클수록, 데이터가 어디서 어떻게 바뀌는 지 일일히 파악하는게 쉽지 않을 수 있기 때문입니다. 물론, 디버깅을 한다면 찾긴 찾겠지만요.
혹시나 이러한 문제가 발생하시면, 당황하지 않고 어디서 문제가 발생하는 지를 빠르게 파악 해야 할 것 같습니다.
이상으로 포스팅을 마치겠습니다. 잘못된 부분이 있다면 댓글 남겨주세요 :)
글이 도움되셨다면, 공감 버튼 한번 눌러주시면 많은 도움이 됩니다 :)
'프로그래밍 > C || CPP' 카테고리의 다른 글
c언어 범위 별 변수 차이를 알아보자 (identifier scope) (0) | 2019.07.17 |
---|---|
부모와 자식클래스 가상 함수의 반환타입을 다르게 재정의 할 수 있을까? (Overriding, Return Value) (0) | 2019.06.27 |
C++ virtual, override, final 키워드별 사용하는 이유를 알아보자. (2) | 2019.06.18 |
[C || C++] Cpplint로 Google C++ Coding Style Guide 체크해보자. (2) | 2019.03.24 |
[C || CPP] C++ 배열 초기화 하는 방법 (fill_n) (0) | 2018.10.14 |
댓글