글 작성자: Sowhat_93

모든 데이터는 바이너리 데이터로 이루어진다.

포인터에 대한 형변환은 언어에게 그냥 해당 주소안에 있는 데이터를 이 형태로 취급해라 라는 정도이다.

그래서 어떤 어드레스에 대해 역참조를 하면 포인터의 형태가 int* 이면 4byte를 읽어들이고,

long long 형이면 8byte를 읽어 들이는 것 뿐이다.

 

다만 이런 캐스트 연산자를 사용하는 이유는

언어 차원에서 뭔가 의도한건 아닌거 같은데? 정도를 잡아내게끔 할때이다.

 

1. static_cast

    int*                    From    = new int;
    void*                   To      = static_cast<double*>(From);
    //No.

    int*                    From    = new int;
    void*                   To      = static_cast<long long*>(From);
    //No.

    long long*              From    = new long long;
    void*                   To      = static_cast<short*>(From);
    //No.

    int*                    From    = new int;
    void*                   To      = static_cast<float*>(From);
    //No.

    double*                 From    = new double;
    void*                   To      = static_cast<float*>(From);
    //No.

    double*                 From    = new double;
    void*                   To      = static_cast<long long*>(From);
    //No.

    double*                 From    = new double;
    void*                   To      = static_cast<int*>(From);
    //Yes.

    float*                  From    = new float;
    void*                   To      = static_cast<int*>(From);
    //Yes.

    long long*              From    = new long long;
    void*                   To      = static_cast<int*>(From);
    //Yes.

static_cast는 컴파일시 잘못된 형변환에 대해 오류를 컴파일 타임에 잡기위해 사용한다.

마치 컴파일러에게 "static_cast 쓴다 ? 네가 보았을때 이상하다 싶으면 알려줘라" 하는 것과 같다.  

그러니 버퍼에서의 오프셋 접근 등 상황에서는 static_cast는 사용하지 않는다.

처음에는 자료형의 크기가 이전 포인터 형보다 이후 포인터형의 실 자료형 크기가 더 크면

형변환을 못하게 막는 줄 알았는데, 그것도 아닌게 long long* 은 float* 로 변환이 안된다.

double* 에서 float* 로의 형변환은 지수부와 가수부의 크기가 달라 자칫 잘못된 값을 역참조 할까봐

막아주는 듯 하다.

double* 에서 int* 는 잘 되는데? 기준은 애매하다.

아무튼 일반적인 상황에서의 실수를 막고 싶다면 static_cast를 사용한다.

 

2. dynamic_cast

런타임시에 상속관계가 명확한지 알아내고 싶을때 사용한다.

포인터 형은 별 의미가 없고, 메모리에 실제로 어떤 값이 있는 지가 중요하다.

상속 관계시에도, private 상속이나 protected 상속의 경우에는 실패한다.

    Parent*     From    = new Child;
    void*       To      = dynamic_cast<Child*>(From);
    //Yes. 

    Child*      From    = new Child;
    void*       To      = dynamic_cast<Parent*>(From);
    //Yes.

    void*       From    = new Child;
    void*       To      = dynamic_cast<Parent*>(From);
    //Yes void* 형인데 왜 되나요? 해당 주소 메모리에 어떤 객체가 있는지를 체크할꺼니까요 ^^.
    //Okay ~

    Parent*     From    = new Parent;
    void*       To      = dynamic_cast<Child*>(From);
    //No. 실제 객체는 상위 클래스이다.
    
    NotAChild*  From    = new NotAChild;
    void*       To      = dynamic_cast<Parent*>(From);
    //No. 상속관계 아님.

    void*       From    = new NotAChild;
    void*       To      = dynamic_cast<Parent*>(From);
    //No. 상속관계 아님. 포인터 형은 중요하지 않다. 메모리에 실제로 그 타입이 들어가 있는가가 중요.

 

3. const_cast

const 를 떼내고 싶을때 사용한다.

    int* Convert = (int*)&A;
    //C Style.

    int* Convert = const_cast<int*>(&A);
    //C++ const.

 

4. reinterpret_cast

사실상 C Style과 같다.

보통 메모리에 접근에 대한 오프셋을 마음대로 지정한다음 특정 바이트 수만큼 해당 형태로 읽게끔 할때사용한다.

    int* A = new int;
    *A = 0x12345678;

    //테스팅 중인 내 머신은 리틀엔디안이다.

    printf("%0x\n", *(reinterpret_cast<char*>(A) + 1));
    //56
    printf("%0x\n", *(reinterpret_cast<char*>(A) + 2));
    //34
    printf("%0x\n", *reinterpret_cast<short*>(A));
    //5678