형변환 Type Conversion 본문
암시적 형변환 Implicit Type Conversion(coersion)
즉 컴파일러가 자동으로 하나의 기본 자료형을 다른 자료형으로 변환한다.
자동 형 변환 automatic type conversion 이라고 불린다.
이전에 변수의 값이 일련의 비트로 저장되는 것과 변수의 자료형은 컴파일러에 비트를 의미있는 값으로 해석하는 방법을 알려준다는 것을 배웠다.
다른 자료형은 "같은"숫자를 다르게 나타낼 수 있다.
예를들어 , int 값 3과 float 값 3.0은 완전히 다른 이진수 패턴으로 저장된다.
float f =3; //Initialize floating point variable with integer 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); //pass integer value 3 to a function expecting a long parameter
-함수에서 다른 자료형의 값을 반환하는 경우
float doSomething()
{
return 3.0; //Return double value 3.0 back to caller through float return type
}
-다른 자료형의 피연산자가 있는 이항 산술 연산자를 사용하는 경우
double division = 4.0/3; //division with a double and an integer
이러한 모든 경우, C++은 데이터를 한 자료형에서 다른 자료형으로 변환하기 위해 형 변환을 한다.
암시적 형 변환은 한 기본 자료형이 예상되지만 다른 기본 자료형이 제공될 때 마다 수행되며,
변환을 수행하는 방법을 컴파일러에 명시적으로 알려주지 않는다.
위에서 언급한 경우들은 모두 암시적 형변환이다.
- 숫자 승격 (numeric promotion)
한 자료형의 값이 더 큰 유사한 자료형의 값으로 변환하는 경우 "숫자 승격(numeric promotion)"이 일어난다.
예를들어, int 는 float로 float는 double로 승격 될 수 있다.
long l(64); //promote the integer 64 into a long
double d(0.12f); //promote the float 0.12 into a double
숫자 승격은 항상 안전하며 데이터 손실이 발생되지 않는다.
- 숫자 변환 (numeric conversion)
큰 자료형의 값이 더 작은 유사한 자료형의 값으로 변환하거나 서로 다른 자료형 간에 변환하는 경우 "숫자 변환(numeric conversion)"이 일어난다.
double d = 3; //convert integer 3 to a double (between different types)
short s = 2; //convert integer 2 to a short (from larger to smaller type)
항상 안전한 숫자 승격과는 다르게 숫자 변환으로 인해 데이터가 손실되거나 그러지 않을 수도 있다.
이 때문에 숫자 변환이 일어나는 모든 암시적 형변환의 코드는 컴파일러에 경고를 보낸다.
다음과 같은 경우들을 주의해야 한다.
1. 범위가 충분히 크지 않은 자료형으로 변환하면 예기치 않는 결과각 발생한다.
int main()
{
int i = 30000;
char c = i;
std::cout << static_cast<int>(c);
return 0;
}
//This prints:
//48
위 코드에서 정수 30000을 char 자료형 (범위: -128 ~ 127)에 할당했다.
이렇게 하면 오버플로가 발생해 원치 않는 결과가 출력된다.
2. 부동소수점 숫자에서 정수로 변환하는 것은 분수 값을 모두 손실시킨다.
int i = 3.5;
std::cout << i;
//This prints
//3 : the fractional value (.5) is lost
- 산술 표현식 평가하기 (Evaluating arithmetic expressions)
표현식을 평가할 때, 컴파일러는 각 표현식을 개별 하위 표현식으로 나눈다.
산술 연산자의 피연산자는 모두 같은 자료형이어야 하므로 컴파일러는 다음과 같은 규칙을 사용한다.
- 피연산자의 자료형이 int 보다 작은 정수인 경우, int 또는 unsigned int 로 승격된다.
- 피연산자의 자료형이 여전히 같지 않으면, 컴파일러는 가장 높은 우선순위 피연산자를 찾고 다른 피연산자를 암시적 형 변환을 통해 일치시킨다.
피연산자의 우선순위는 다음과 같다.
- long double (highest)
- double
- float
- unsigned long long
- long long
- unsigned long
- long
- unsigned int
- int (lowest)
example 1
#include <iostream>
#include <typeinfo> //for typeid()
int main()
{
short a(4);
short b(5);
std::cout << typeid(a + b).name() << ":" << a + b << std::endl; //show us the type of a + b
return 0;
}
//This prints
//int:9
short는 정수이기 때문에, 더하기가 수행되기 전에 int로 승격된다.
두 개의 int를 더한 결과는 int 다.
example 2
#include <iostream>
#include <typeinfo> //for typeid()
int main()
{
double d(4.0);
short s(2);
std::cout << typeid(d + s).name() << ":" << d + s << std::endl; //show us the type of d + s
return 0;
}
//This prints
//double : 6.0
short는 int로 승격된다. 그러나 두 피연산자는 여전히 일치하지 않기 때문에 int 보다 우선순위가 더 높은 double로 승격된다.
두 개의 double을 더한 결과는 double 이다.
example 3
문제를 일으킬 수 있다.
std::cout << 5u - 10; //5u means treat 5 as an unsigned integer
//This prints
//4294967291
이 같은 경우 우선순위에 의해서 부호없는 정수(unsigned int)로 승격되고 결과값의 오버플로로 인해 예상치 못한 결과를 얻을 수 도 있다.
위 예제는 부호 없는 정수를 피하는 많은 이유 중 하나다.
명시적 형변환 Explicit Type Concersion(casting)
형 변환을 하기 위해 형 변환 연산자(type casting operator)를 사용한다.
초보 프로그래머들은 float f = 10/4; 와 같은 걸 시도한다.
그러나 10과 4는 모두 정수이므로 "숫자 승격"이 일어나지 않는다.
정수값 나누기 표현식 10/4 는 2의 값을 생성하고, 그 값은 암시적으로 2.0으로 변환되어 변수 f에 할당된다.
위 같은 경우에는 정수 리터럴 값(ex. 2, 4)중 하나를 부동 소수점 숫자 리터럴 값(Ex. 2.0, 4.0)으로 바꾸면
나누기는 부동 소수점 숫자 나누기로 수행될 것이다.
하지만 변수를 사용하는 경우 어떻게 될까?
int i1 = 10;
int i2 = 4;
float f = i1/i2;
변수 f 의 값은 2로 끝난다.
정수 나누기 대신 부동 소수점 숫자 나누기를 한다고 컴파일러에 어떻게 말할까?
답은 " 형 변환 연산자"를 사용해야 한다.
형 변환 연산자를 사용해서 컴파일러에 명시적 형 변환을 하도록 지시해야 한다.
C Style Cast
표준 C 프로그래밍에서는 () 연산자에 변환할 자료형의 이름을 사용해 형 변환한다.
int i1 = 10;
int i2 = 4;
float f = i1/i2;
위 프로그램에서는 컴파일러가 i1을 부동 소수점 값으로 변환하기 위해서 float를 사용한다.
i1은 이제 부동 소수점 값이므로 i2는 부동 소수점값으로 변환되며, 정수 나누기 대신 부동 소수점 나누기를 수행한다.
C++에서는 다음과 같이 함수적인 C Style Cast를 사용할 수 있다.
int i1 = 10;
int i2 = 4;
float f = i1/i2;
C-style cast는 컴파일 시간에 검사되지 않으므로 오용될 수 있다. (const를 제거하는 등)
그러므로 C-style cast는 일반적으로 피하는 게 좋다.
static_cast
C++에서는 static_cast라는 형 변환 연산자를 도입했다.
char c = 'a';
std::cout << static_cast<int>(c) << std::endl;
//This prints
//97
static_cast 는 하나의 자료형을 다른 자료형으로 변환하는데 가장 좋은 방법이다.
int i1 = 10;
int i2 = 4;
float f = static_cast<float>(i1)/i2;
static_cast의 주요 장점은 컴파일 타임에 타입 검사를 제공하여 부주의한 오류를 만들기가 더 어렵다는 것이다. satic_cast는 C-style cast보다 덜 강력해서 실수로 const를 제거하는 등 의도하지 않은 작업을 할 확률을 줄여준다.
컴파일러는 안전하지 않은 암시적 형 변환을 할 때마다 불평한다.
int i = 48;
char ch = static_cast<char>(i); //implicit conversion
int(4-byte)를 char(1-byte)로 변환하면 잠재적으로 안전하지 않으므로 컴파일러는 불평한다.
이 형 변환을 사용자가 인지하고 사용한다는 것을 알리기 위해 다음과 같이 형 변환 연산자를 사용하면 된다.
int i = 48;
char ch = static_cast<char>(i);
다음 프로그램에서 컴파일러는 double 을 int로 변환하면 데이터 손실이 발생할 수 있다고 불평한다.
int i = 100;
i = i/2.5;
명시적인 의미를 컴파일러에 알려주면 된다.
int i = 100;
i = static_cast<int>(i/2.5);
형 변환을 할 때마다 문제가 발생할 가능성이 있으므로 가능하면 형 변환을 피해야 한다.
그러나 피할 수 없는 경우, C style cast 대신 C++의 static_cast 를 사용해야 한다.
'💘 C++ > 변수범위, 변수형' 카테고리의 다른 글
열거형 enumerated types (0) | 2022.01.23 |
---|---|
문자열 std::string (0) | 2022.01.23 |
auto 키워드와 자료형 추론 (0) | 2022.01.16 |
Using 문과 모호성 Ambiguity (0) | 2022.01.15 |
전역 변수, 정적 변수, 내부 연결, 외부 연결 Global Variable, Static Variable, Internal Linkage, External Linkage (0) | 2022.01.14 |