묵시적 형변환

자, 다음 코드에서 뭔가 이상한 점을 찾아보세요.

#include <stdio.h>
int main() {
      int a = 6.5;
      printf("%d\n", a);
      return 0;
}

6

자, 어떻게 int 자료형에 6.5를 담았을까요?

C 언어에서는 서로 맞지 않는 자료형 간의 연산 중 일부에 대한 대책을 세워놓았습니다. 이것이 바로 묵시적 형변환입니다.

묵시적 형변환에는 몇 가지 규칙이 있습니다.

첫째, 정수 자료형에 실수 자료형을 담으면 소수점 아래는 버려집니다. 위에서 본 것이 그 예시 중 하나이지요. 단,

#include <stdio.h>
int main() {
      printf("%d\n", 6.5);
      return 0;
}

이건 조금 곤란합니다. 이건 printf의 작동 방식과 관련된 부분이기 때문에, 지금은 이렇게 하면 안된다는 것만 알아두세요.

둘째, 실수 자료형에 정수 자료형을 담으면 .0이 생깁니다.

#include <stdio.h>
int main() {
      float a = 6;
      printf("%f\n", a);
      return 0;
}

6.000000

셋째, 작은 자료형에 큰 자료형의 값을 대입하면 하위 n바이트만 가져옵니다.

#include <stdio.h>
int main() {
      short a = 0x12345678;
      printf("%hX\n", a);
      return 0;
}

5678

넷째, 양쪽 다 int보다 작은 정수형일 경우 연산의 결과는 int형입니다.

#include <stdio.h>
int main() {
      short a = 10, b = 5;
      printf("%d...%d\n", a + b, sizeof(a + b));
      return 0;
}

15...4

다섯째, 양쪽 다 정수형이거나 양쪽 다 실수형일 경우 연산의 결과는 둘 중 더 범위가 넓은 자료형이 됩니다. 참고로 상수 뒤에 ll을 붙이거나 u를 붙이면 각각 long longunsigned입니다. 조합해서 ull을 만들면 unsigned long long이 되고, 아무것도 없으면 그냥 int입니다.

#include <stdio.h>
int main() {
      printf("%d...%d\n", 10ll + 10, sizeof(10ll + 10));
      return 0;
}

20...8

여섯째, 부호 없는 정수형과 부호 있는 정수형일 경우 연산의 결과값은 부호 없는 정수형이 됩니다.

#include <stdio.h>
int main() {
      unsigned int a = 0;
      for(int i = 0; i < a - 1; ++i)
            printf("*\n");
}

*

*

*

*

...

산술 오버플로우로 인해 a - 1의 값은 4294967295가 되고, 결국 4294967295개의 *이 출력됩니다. aint였다면 한 개도 출력되지 않았을 텐데 말이죠.

말 나온 김에, 컴퓨터에서의 음수와 오버플로우에 대해서도 설명하죠. 컴퓨터는 음수를 저장할 때, '2의 보수'라는 방법으로 저장합니다. 모든 비트에 NOT연산을 취한 뒤, 1을 더해주는 거죠. 이렇게 하면 양수와의 덧셈도 자연스럽게 가능하고, 뺄셈도 쉬우니까요.(a - ba + (-b)로 바꿔서 계산). 맨 오른쪽(상위) 비트는 부호 식별용으로 사용됩니다.

주유소에서 기름을 100만원어치 넣으면 계기판이 999997원, 999998원, 999999원 하고 올라가다 000000원이 되는 것을 상상해 본 적이 있습니까? 이런 것을 '오버플로우'라고 합니다. char 자료형을 생각해 봅시다. 현재의 값은 0111 1111(2)입니다. 여기서 1을 더하면 1000 0000(2)입니다. 아, 양수가 너무 커지다 보니 음수가 되었습니다. unsigned였다면 1111 1111(2)에서 커지다 못해 0000 0000(2)이 되었겠지요.