본문 바로가기

# Lang/C++

2.3 자료형과 형변환

 형변환 (Type Conversions)

어떤 데이터 타입이 다른 데이터 타입으로 변환되는 작업을 형변환이라고 하며,

누가 의도했는지에 따라 암묵적, 명시적 형변환으로 분류된다.



형변환의 종류

  • 암묵적 변환 (컴파일러가 형변환을 의도함.)
  • 명시적 변환 (프로그래머가 형변환을 의도함.)




 암묵적 변환  (Implicit Conversions)

T1 형식의 데이터를 T2 형식의 변수에 대입하려고 하는 경우,

컴파일러는 두 자료형 사이의 변환연산를 찾고, 프로그래머의 지시 없이 형변환을 진행한다.



Example :

int a = 3;
double b = a;

프로그래머가 의도적으로 형변환 연산자를 사용하지 않았지만,

컴파일러는 암묵적으로 int를 double로 변환하는 연산을 찾고 형변환을 진행한다.


만약, 컴파일러가 올바른 변환연산을 찾지 못했다면,

컴파일러는 형변환을 진행할 수 없다는 오류를 발생시킨다.





 명시적 형변환 (Implicit Conversion)

T1 형식의 데이터를 T2 형식으로 바꾸고자 할 때 사용한다.

프로그래머는 명시적으로 캐스트 연산자를 적어 형변환을 의도할 수 있는데,
이것을 명시적 형변환 또는 캐스팅이라고 한다.



Example :

int a = 3;
double b = (int) a;  // C-style Casting.
위의 예시에서는 int 형식의 데이터를 double로 변환하기 위해,
프로그래머가 의도적으로 캐스팅 연산자를 사용하여 형변환을 진행했다.

결과적으로 double 데이터가 double 변수에 저장된 것이므로, 
암묵적 형변환과는 성질이 다르다. (암묵적 형변환은, 다른 타입의 대입연산에서 발생.)




 문맥적 변환 (Contextual Conversions)

문맥에 따라 컴파일러가 진행하는 암묵적 변환.

아래와 같은 상황을 예로 들 수 있다.

  • if(expr)에서 expr은 bool로 형변환됨.
  • switch(expr)에서 expr은 int로 형변환됨.
  • ...





 표준 변환  (Standard Conversions)

C++에서는 원시형 사이의 변환연산이 미리 정의되어 있으며, 이것을 표준 변환이라고 한다.
암묵적이든 명시적이든 형변환이 발생한다면, 컴파일러는 표준 변환부터 찾는다.


대표적인 표준 변환의 종류는 다음과 같다 :
  • 정수 확장  (Integral Promotions)
  • 정수 변환  (Integral Conversions)
  • 부동소수 변환  (Floating-Point Conversions)
  • 부동소수-정수 변환  (Floating-Point and Integer Conversions)
  • 수학적 변환  (Arithmetic Conversions)
  • 포인터 변환  (Pointer Conversions)
  • 정수형 상수-포인터 변환
  • ... 


1. 정수 확장  (Integral Promotions)
작은 정수는 더 큰 정수 타입으로 확장될 수 있다.
정수 확장이 발생할 수 있는 타입은 다음과 같다.
  • char,  short int
  • int bit fields
  • Enumerators
short a = 3;
int b = a;



2. 정수 변환  (Integral Conversions)
signed와 unsigned는 안전하지 않지만 상호변환될 수 있다.
  • Signed to unsigned.
  • Unsigned to signed.
int a = -1;
unsigned int b = a;



3. 부동소수 변환  (Floating-Point Conversions)
더 넓은 범위의 타입으로 변환될 때는, 손실없이 안전하지만,
반대의 경우에는 손실을 고려해야 한다.
  • float to (double, long double)
  • (double, long) double to float
double a = 0.3f;
float b = 0.3lf;



4. 부동소수-정수 변환  (Conversions between Integral and Floating-Point types)

정수가 부동소수로 변환될 때는,  정확하지 않은 부동소수로 변환되며,  원래 값보다 클수도 작을수도 있다.

부동소수가 정수로 변환될 때는,  소수점이 절삭된다.

int a = 3;
double b = a;  //  b == (3.0+delta  OR  3.0-delta)

double x = 2.9;
int y = x;  // y == 2



5. 수학적 변환  (Arithmetic Conversions)

수학적인 계산 도중에 발생하고, 작은 자료형을 넓은 범위의 자료형으로 변환한다.

아래의 매커니즘을 순서대로 진행한다.

  • 다른 수학적 타입(정수, 부동소수)을 만나면, 부동소수로 통일한다.
  • 같은 수학적 타입(정수, 부동소수)을 만나면, 범위가 큰쪽으로 통일한다.
  • 같은 수학적 타입이지만 부호형식이 다른 경우,  unsigned 타입으로 통일한다.
Example :
double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}



6. 포인터 변환  (Pointer Conversions)

포인터 변환은 아래의 상황을 가르킨다.

  • 포인터를 객체로,  (Pointer to Classes)   // 접근 지시자와 다형성에 영향을 받는다.
  • 포인터를 함수로,  (Pointer to Function)  // 함수 포인터
  • 포인터를 보이드 타입으로,  (Pointer to void)

추가적으로, 모든 포인터 타입은 void*로 변환될 수 있고,

void*는 모든 포인터 타입으로 변환될 수 있다.


이 때문에 void*는 범용 포인터 자료형(generic pointer type)이라고 불리기도 하는데,

템플릿이 없는 C-API는 void*를 인자로 받고, 다른 자료형으로 변환한다.

double a = 3.14;
char* b = (char*)(void*)&a;   // Compile Success.  but,  Runtime Error occur.



7. 정수형 상수-포인터 변환  (Integral constant-Pointer Conversions)

상수 0은 null pointer와 동일하다.  (C++11 부터는 nullptr을 이용하자.)

int* i_ptr = 0;  
int* i_ptr = nullptr;   // since c++11.   nullptr != 0(NULL Pointer)





사용자 정의 형변환  (User-Defined Type Conversions)

명시적 형변환시,  표준 변환에서 일치하는 변환연산을 찾지 못했다면.

사용자가 정의한 형변환 연산이 있는지 검사한다.



Example :

class Integer{
public:
    int data = 0;
    explicit Integer(int _data):data{_data}{};

    // User-Defined Type Conversions.
    // Integer to int.
    explicit operator int() const {return data;}
};


int main() {
    int a = (int)Integer(3);    // 3
    return 0;
}

사용자 정의 형변환에서도 올바른 변환연산을 찾지 못했다면,
컴파일러는 컴파일 에러를 발생시킨다.

암묵적 형변환시에는 사용자 정의 형변환을 검사하지 않으므로,
int a = Integer(3);  은 에러를 발생시킨다.





 C++ 캐스팅 연산자

C++은 다음의 캐스팅 연산자를 지원한다.

  • static_cast<T>
  • dynamic_cast<T>
  • reinterpret_cast<T>
  • const_cast<T>


static_cast<T>

C-style의 캐스팅과 동일한 역할을 한다.

컴파일 타임에 형변환을 검사하고, 일치하는 변환연산을 진행한다.

런타임에는 검사하지 않는다.



dynamic_cast<T>

다형성을 이루는 클래스의 포인터에만 적용되는 캐스팅 연산자.

다형성을 거스르는 변환을 진행하면 nullptr을 반환한다.

런타임에 검사하며,  컴파일 타임에 검사하지 않는다.



reinterprete_cast<T>

비트열은 건들지 않고, 타입 자체만 바꾸는 캐스팅 연산자.

타입 자체만 바꾸기 때문에,  컴파일 에러는 발생시키지 않는다.

단, 올바르지 않은 자료형으로 바꾸고 사용한 경우 런타임에러가 발생한다.




const_cast<T>

const, volatile과 같은 제한자를 붙이거나 뗄 수 있는 캐스팅 연산자.





 참고문헌

'# Lang > C++' 카테고리의 다른 글

3.2 기억영역 클래스 지정자  (0) 2019.04.06
3.1 CV 제한자 (const, volatile)  (0) 2019.03.31
2.2 자료형과 데이터 해석  (0) 2019.03.27
2.1 자료형  (0) 2019.03.26
1.3 변수와 메모리 모델  (0) 2019.03.23