Quick Reference
C 형변환은 문법보다 변환이 일어나는 시점이 결과를 바꿉니다. 정수 나눗셈 전에 실수로 바꿀지, 결과 뒤에 바꿀지를 코드로 나란히 비교하면 됩니다.
int total = 7;
int count = 2;
double a = (double)total / count; // 3.5
double b = (double)(total / count); // 3.0signed와 unsigned를 섞으면 비교 전에 공통 타입으로 바뀔 수 있습니다. 음수와 크기 값을 비교해야 하면 음수 여부를 먼저 처리한 뒤 같은 부호의 타입으로 비교합니다.
int n = -1;
unsigned limit = 1u;
/* n < limit 은 기대와 다를 수 있음 */
if (n >= 0 && (unsigned)n < limit) {
/* 범위를 확인한 뒤 비교 */
}큰 값을 작은 타입에 넣거나 포인터를 cast하는 일은 안전해지는 과정이 아닙니다. 목적 타입의 범위, 포인터의 정렬·수명·객체 크기가 이미 맞는지 확인한 뒤에만 변환합니다.
문법
캐스팅은 단순히 “이 타입으로 바꾼다”가 아니라 언제 변환이 일어나는가를 같이 봐야 합니다.
- 앞은 나누기 전에 실수로 바뀜
- 뒤는 정수 나눗셈이 끝난 뒤에 바뀜
실전에서는 "언제 변환되느냐"가 중요합니다.
손실 구간
int n = -1;
unsigned int u = 1;
if (n < u) {
/* 기대와 다를 수 있음 */
}이 문제는 비교 전에 타입이 맞춰지기 때문입니다. 음수가 큰 양수처럼 해석될 수 있어서 결과가 뒤집힙니다.
즉 unsigned가 끼는 순간 비교식은 더 조심해서 읽어야 합니다.
작은 정수 타입은 보통 int 또는 unsigned int로 먼저 승격되고, 그 뒤 두 피연산자가 공통 타입으로 변환됩니다. 정확한 결과가 중요한 비교는 두 값의 범위를 먼저 확인하고, 같은 signed 타입으로 설계하는 편이 캐스트보다 안전합니다.
double d = 3.9;
int n = (int)d; // 3
int big = 300;
char c = (char)big;캐스트는 "안전하다"는 뜻이 아니라 "내가 이 손실을 감수한다"는 표시입니다.
실수를 정수로 바꾸면 소수 부분은 0 쪽으로 버려집니다. 변환 전 값이 목적 정수 타입으로 표현할 수 없는 범위면 동작을 기대할 수 없으므로, 외부 값은 캐스트 전에 범위를 확인합니다.
변환 규칙
char a = 100;
char b = 20;
int sum = a + b;여기서는 char끼리 계산하는 것이 아니라, 보통 int로 승격된 뒤 계산합니다. 그래서 C는 표면상 작은 타입을 써도 연산 안에서는 더 큰 타입으로 움직이는 경우가 많습니다.
포인터 캐스트도 주소를 안전하게 만들지는 않습니다. 정렬, 실제 객체의 수명, 가리키는 저장 공간의 크기가 맞지 않으면 캐스트 뒤 역참조가 잘못될 수 있습니다. 서로 다른 객체 포인터를 일반 변환 도구처럼 쓰지 않습니다.
빠른 점검
- 정수 나눗셈 결과를 실수로 바꾸고 싶을 때
- 포인터 타입을 명시적으로 보여주고 싶을 때
- 범위 축소를 의도적으로 드러내고 싶을 때
unsigned가 섞이면 비교 결과를 다시 확인- 작은 정수 타입도 연산 전에 승격될 수 있음
하지만 캐스트가 많아질수록 "원래 타입 설계가 불편한 것 아닌가"도 같이 의심해야 합니다.
주의할 점
캐스트는 버그를 고치는 도구가 아니라, 변환을 강제로 통과시키는 도구이기도 합니다. 경고를 없애려고 캐스트부터 넣기 시작하면 오히려 더 위험해집니다. 먼저 왜 타입이 안 맞는지 보는 편이 맞습니다.
참고 링크
2 sources