05.01 - 암시적 형 변환 (Implicit type conversion)
이전에 변수의 값이 일련의 비트로 저장되는 것과 변수의 자료형은 컴파일러에 비트를 의미 있는 값으로 해석하는 방법을 알려준다는 것을 배웠다. 다른 자료형은 "같은" 숫자를 다르게 나타낼 수 있다. 예를 들어, int 값 3과 float 값 3.0은 완전히 다른 이진수 패턴으로 저장된다.
float f = 3;
위의 경우 컴파일러는 값 3을 나타내는 비트만 float f로 할당할 수 없다. 대신 정수 3을 부동 소수점 숫자로 변화해야 하며, 이 값을 할당할 수 있다.
한 자료형에서 다른 자료형으로 값을 변환하는 것을 형 변환이라고 한다. 형 변환은 여러 가지 경우에 발생할 수 있다.
- 다른 자료형 값으로 변수 초기화 또는 할당하는 경우:
double d(3); // initialize double variable with integer value 3
d = 6; // assign double variable with integer value 6
- 함수 매개 변수에 다른 자료형의 값을 전달하는 경우:
void doSomething(long l)
{
}
doSomething(3);
- 함수에서 다른 자료형의 값을 반환하는 경우:
float doSomething()
{
return 3.0; // Return double value 3.0 back to caller through float return type
}
- 다른 자료형의 피연산자가 있는 이항 산술 연산자를 사용하는 경우:
double division = 4.0 / 3;
이러한 모든 경우, C++은 데이터를 한 자료형에서 다른 자료형으로 변환하기 위해 형 변환을 한다.
기본적인 형 변환에는 두 가지 유형이 있다.
- 암시적 형 변환(implict type conversion): 즉 컴파일러가 자동으로 하나의 기본 자료형을 다른 자료형으로 변환한다. 자동 형 변환(automatic type conversion)이라고 불린다.
- 명시적 형 변환(explict type conversion): 형 변환을 하기 위해 형 변환 연산자(type casting operator)를 사용한다.
이 포스트에서는 우선 암시적 형 변환을 다루고, 다음 포스트에서 명시적 형 변환을 다룰 예정이다.
암시적 형 변환
암시적 형 변환은 한 기본 자료형이 예상되지만 다른 기본 자료형이 제공될 때마다 수행되며, 변환을 수행하는 방법을 컴파일러에 명시적으로 알려 주지 않는다. 위에서 언급한 경우들은 모두 암시적 형 변환이다.
한 자료형의 값이 더 큰 유사한 자료형의 값으로 변환하는 경우 "숫자 승격(numeric promotion)"이 일어난다. 예를 들어, int
는 floa
t으로 float
은 double
로 승격될 수 있다.
long l(64);
double d(0.12f);
숫자 승격은 항상 안전하며 데이터 손실이 발생하지 않는다.
- 숫자 변환 (numeric conversion)
큰 자료형의 값이 더 작은 유사한 자료형의 값으로 변환하거나 서로 다른 자료형 간에 변환하는 경우 "숫자 변환(numeric conversion)"이 일어난다.
double d = 3;
short s = 2;
항상 안전한 "숫자 승격"과는 다르게 숫자 변환으로 인해 데이터가 손실되거나 그러지 않을 수도 있다. 이 때문에 숫자 변환이 일어나는 모든 암시적 형 변환의 코드는 컴파일러에 경고를 보낸다. 다음과 같은 경우들을 주의해야 한다.
1. 범위가 충분히 크지 않은 자료형으로 변환하면 예기치 않는 결과가 발생한다.
int main()
{
int i = 30000;
char c = i;
std::cout << static_cast<int>(c);
return 0;
}
위 코드에서 정수 30000을 char
자료형(범위: -128 ~ 127)에 할당했다. 이렇게 하면 오버플로가 발생해 원치 않는 결과가 출력된다.
2. 부동 소수점 숫자에서 정수로 변환하는 것은 분수 값을 모두 손실시킨다.
int i = 3.5;
std::cout << i;
산술 표현식 평가하기 (Evaluating arithmetic expressions)
표현식을 평가할 때, 컴파일러는 각 표현식을 개별 하위 표현식으로 나눈다. 산술 연산자의 피연산자는 모두 같은 자료형이어야 하므로 컴파일러는 다음과 같은 규칙을 사용한다.
- 피연산자의 자료형이 int보다 작은 정수인 경우, int또는 unsigned int로 승격된다.
- 피연산자의 자료형이 여전히 같지 않으면, 컴파일러는 가장 높은 우선순위 피연산자를 찾고 다른 피연산자를 암시적 형 변환을 통해 일치시킨다.
피연산자의 우선순위는 다음과 같다.:
- long double (highest)
- double
- float
- unsigned long long
- long long
- unsigned long
- long
- unsigned int
- int (lowest)
예제 1
#include <iostream>
#include <typeinfo>
int main()
{
short a(4);
short b(5);
std::cout << typeid(a + b).name() << " : " << a + b << std::endl;
return 0;
}
short
는 정수이기 때문에, 더하기(+
)가 수행되기 전에 int
로 승격된다. 두 개의 int
를 더한 결과는 int
다.
예제 2
#include <iostream>
#include <typeinfo>
int main()
{
double d(4.0);
short s(2);
std::cout << typeid(d + s).name() << " : " << d + s << std::endl;
return 0;
}
short
는 int
로 승격된다. 그러나 두 피연산자는 여전히 일치하지 않기 때문에 int
보다 우선순위가 더 높은 double
로 승격된다. 두 개의 double
을 더한 결과는 double
이다.
예제 3
문제를 일으킬 수 있다.
std::cout << 5u - 10;
이같은 경우 우선순위에 의해서 부호없는 정수(unsigned int)로 승격되고 결과값의 오버플로로 인해 예상치 못한 결과를 얻을 수도 있다.
위 예제는 부호 없는 정수를 피하는 많은 이유 중 하나다.
번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/44-implicit-type-conversion-coercion/ 입니다.