본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] Explicit Casting (명시적 캐스팅)

by 심찬 2021. 8. 14.

 

 

C++ Explicit Casting (C++ 명시적 캐스팅)

 

C의 캐스팅이 논리적이지 않고, 위험성이 있으며, 버그 가능성이 높다.

그래서 C++에서는 새로운 캐스팅 방법을 소개하고 있다.

 

C언어의 캐스팅 방법 : 

 int* p1 = (int*)malloc(sizeof(int)*10);    // C언어의 캐스팅 방법 : C++에서도 사용은 가능하다.

 

C++의 캐스팅 방법 : 

#include <iostream>
#include <cstdlib>

int main()
{    
    int* p1 = static_cast<int*>(malloc(sizeof(int)*10));  // C++ 에서 사용하는 캐스팅 방법

    free(p1);
}

 

 

C언어 방식의 캐스팅의 단점 :

   아래 두 가지 경우가 대표적인 c언어에서 발생할 수 있는 캐스팅 문제이다.

 

단점 예제 (1) :

#include <iostream>

int main()
{
    int n = 0;  
    
    //double* p1 = &n; // error
    
    double* p1 = (double*)&n;  // ok

    *p1 = 3.4;     
}

>> int 형은 4byte, double 형은 8byte 이지만 각 데이타를 가리키는 포인터형 주소값은 4byte 이기 때문에 (double*)를 사용한 C언어 캐스팅으로 형변환이 가능하다.

문제는 *p1 (p1이 가리키는 곳)에 3.4 (더블형 값) 을 넣게 되면 문제가 발생하게 된다.

시스템이 오류를 내서 죽을 수도 있는데, 다른 곳의 값을 변경하게 되는 불상사가 발생할 수 있다. (예측할 수 없는 에러가 발생하게 된다.)

 


단점 예제 (2) :

 

#include <iostream>

int main()
{
    const int c = 10;
    
    //int* p2 = &c;  // error 
    int* p2 = (int*)&c;  // ok
    
    *p2 = 20; 
    
    std::cout << c << std::endl; // 10 ? 20
}

>>  const로 정의된 c 가 10 으로 정의했는데, (int *) 형변환을 통해 int* p2로 캐스팅되어 들어갈 수 있다.

*p2를 20으로 변경했다면 과연 c를 출력했을 때 무슨 값을 출력해야 하는가.

임배디드 코딩에서는 해당 캐스팅이 필요한데, 문제는 개발자 의도인지 실수인지 구별할 수가 없다는데 있다.

 

실행해보면 c 값으로 10이 출력된다. 오히려 실행이 되어 더 무서운 경우이다. 나중에 어떤 문제를 가져올지 모른다.

 

 


C++에서의 캐스팅은?

 - C++에서는 용도별로 다른 캐스팅 방법을 사용한다.

 - 용도 별로 4개의 캐스팅 연산자를 제공한다.

 

 

static_cast

 - 가장 기본적인 캐스팅 연산자

 - 정수와 실수 사이의 변환, 열거형과 정수 사이의 변환

 - void* -> 다른 타입 *변환

 - 배열 -> 포인터,  함수-> 함수 포인터 등

 - 상수성을 제거하거나 서로 다른 타입의 포인터 끼리의 변환은 허용하지 않는다. (단, 상속 관계는 허용한다.)

#include <cstdlib>    // malloc, free 함수 선언된 헤더

void foo(int)    {}
void foo(double) {}

int main(void)
{
    const int c = 10;
    double d = 3.4;
    
    int  n  = static_cast<int>(d);               // 실수를 정수로 변환, double 을 int로 변환
    int* p1 = static_cast<int*>(malloc(100));    // void* 를 다른 타입으로 변환 가능
    
    auto p2 = static_cast<void(*)(int)>( &foo);  // 함수 이름이 오버로딩 되어 있을 때,
    
    int* p3 = static_cast<int*>(&d); // error :  서로 다른 타입의 포인터 변환은 허용하지 않는다.
    int* p4 = static_cast<int*>(&c); // error :  상수성을 제거하는 캐스팅은 허용하지 않는다.
}

 

main.cpp:15:35: error: invalid static_cast from type ‘double*’ to type ‘int*’

main.cpp:16:35: error: invalid static_cast from type ‘const int*’ to type ‘int*’

 

 

 


reinterpret_cast

 - 서로 다른 포인터 타입 끼리의 변환

 - 정수와 포인터 사이의 변환

 

아래와 같이 double포인터 에 int n의 주소를 넣는 코드는 위험하며, 아래와 같이 error를 발생시킨다.

static_cast가 아닌 reinterpret_cast로 변환이 가능하다.

int main()
{
	int n = 10;
	double* p1 = &n;    //  어떻게 캐스팅이 가능할까?
    
	int* p2 =10;       // (임배디드 코딩) 주소값을 강제로 넣고 싶은 경우!
}

 

 

위의 코드를 아래와 같이 reinterpret_cast로 가능하다. 사용에 유념해야 한다.

  
int main()
{
    int n = 10;
    
    double* p1 = reinterpret_cast<double*>(&n);
    
    int* p2 = reinterpret_cast<int*>(10);
}

주의!

정수 n을 d로 변경하기 위해 reinterpret_cast를 사용하면 아래와 같은 에러가 발생한다. 위에 제시된 두 가지 용도 이외로 사용이 불가하다.

int main()
{
	double d = reinterpret_cast<double>(n);
}

 


const_cast

포인터와 참조형의 상수성(const)과  volatilie 속성을 제거하는 데 사용

 

reinterpret_cast는 서로 다른 타입끼리 변환은 가능하지만 const가 붙어 있으면 안된다.

 

int main()
{
    const    int c = 10;
    volatile int v = 20;
    
    int n = c;
    
    // const가 있는 &c를 캐스트 할 수 있는 유일한 캐스트는 const_cast뿐
    int* p1 = const_cast<int*>(&c); 
    
    // volatile 있는 것 역시 const_cast로만 캐스트 가능
    int* p2 = const_cast<int*>(&v);
    
    // const_cast는 const, volatile 캐스팅 용도 이외의 다른 것은 불가하다.
    double* p3 = const_cast<double*>(&c);  // error
}

 

main.cpp:15:40: error: invalid const_cast from type ‘const int*’ to type ‘double*’

 

 

예제) const int 형 변수의 주소를 char*  변수에 담아보기

int main()
{
    const int c = 10;
    
    //char* p = static_cast<char*>(&c);    // const 속성 제거 불가, int 형-> char* 불가
    
    //char* p = const_cast<char*>(&c);     // const 제거 가능, int 형-> char* 불가
    
    //char* p = reinterpret_cast<char*>(&c);  // const 속성 제거 불가, int 형-> char* 가능
    
    
    // ======== 두번의 캐스팅으로 가능함!  ==========
    
    char* p1 = reinterpret_cast<char*>(             // 2) int를 char* 포인터로 변경
                        const_cast<int*>( &c ) );   // 1) const int 속성에서 const 제거
                        
    
    // 위의 방법과 반대의 순서로도 가능하다. 단, const 를 달고 있는 채로 수행해야 함.
    char* p2 = const_cast<char*>(
                reinterpret_cast<const char*>( &c ) );
    
    
    // 이 방법도 가능하나, 개발자의 의도가 명확하지 않음.
    char* p3 = (char*)&c;
    
    
    // c++이 c에 비해 코드가 복잡하다는 단점아닌 단점....
                        
}

 

 

 

 

 

 


dynamic_cast

안전한 다운 캐스팅 (기반 클래스 포인터를 안전하게 파생 클래스 타입의 포인터로 캐스팅할 때 사용)

실행 시간 캐스팅 - 실행 시간 오버헤드가 존재

>> 나중에 별도 포스팅으로~!!

댓글