본문 바로가기
프로그래밍/C || CPP

[C/C++] unsigned char 타입에 음수를 넣은 값이 signed short, int로 형변환 하면 이상한 값으로 변하는 이유

by _BlankSpace 2019. 7. 6.

혹시, 프로그래밍을 하다가 분명히 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으로 추가되는 것을 확인할 수 있습니다.

 

글을 마치며..

결국, 이러한 문제를 해결하기 위해서는 형변환을 할 때, 해당 값이 부호가 없는 타입인 지, 부호가 있는 타입인 지를 확인하여 사용 하는 것이 중요하다고 생각합니다.

 

프로그램이 클수록, 데이터가 어디서 어떻게 바뀌는 지 일일히 파악하는게 쉽지 않을 수 있기 때문입니다. 물론, 디버깅을 한다면 찾긴 찾겠지만요.

 

혹시나 이러한 문제가 발생하시면, 당황하지 않고 어디서 문제가 발생하는 지를 빠르게 파악 해야 할 것 같습니다.

 

이상으로 포스팅을 마치겠습니다. 잘못된 부분이 있다면 댓글 남겨주세요 :)

글이 도움되셨다면, 공감 버튼 한번 눌러주시면 많은 도움이 됩니다 :)

댓글