프로그래밍/C++

C++] reinterpret_cast에 대해서...

Hwan2 2020. 6. 17. 01:12
반응형

모든 언어에는 형변환이 있습니다. C++에선 다양한 형번환 객체들을 제공합니다.


1. static_cast = https://hwan-shell.tistory.com/211


2. dynamic_cast = https://hwan-shell.tistory.com/213


3. const_cast = https://hwan-shell.tistory.com/215


4. reinterpret_cast


가 있습니다.


그 중 reinterpret_cast에 대해 알아보도록 하겠습니다.



1. reinterpret_cast란?

cast중 가장 쎈놈? 입니다. cast방식은 포인터 -> 포인터, 포인터 -> 일반변수, 일반변수 -> 포인터로
주로 포인터 관련된 cast입니다. 단, 자료형 -> 다른 자료형은 안됩니다.

reinterpret_cast는 형변환이 이뤄지게 되면 해당 자료형의 bit수에 맞게 들어가게 됩니다.
하지만 int * -> char -> int *로 간다면?? 주소값이 파괴되어 nullptr을 가르키게 됩니다.
char형은 1바이트크기여서 주소 값을 다 표현하지 못하기 때문입니다.
이는 reinterpret_cast에 특징 떄문인데, 이녀석은 자료를 그대로 변수로 전달하게 됩니다.(bit 단위로)
때문에 int * -> unsigned int or long -> int * 같은 경우에는 데이터가 그대로 유지되게 됩니다.
주소 값을 모두다 채울 수 있는 크기이기 때문이죠. 하지만 char는 바이트 수가 작기 때문에 데이터를 옮기는 과정에서
dump가 되버려 원본 데이터가 파괴 되는 것이죠.

이녀석은 (void *)로도 전달이 가능하기 때문에 특수한 경우에 쓰입니다.
주로 패킷통신할 때 자료를 포인터로 받아올 때 reinterpret_cast를 사용하게 되며 그 외엔 잘 사용하지 않는걸로 알고 있습니다.
사용가능한 범위를 생각해 본다면.... C <-> C++간의 low레벨에서 사용할 것 같네요....


2. 예시

1) int * -> char -> int *

int main() {
    int* p = new int(100);
    char c = reinterpret_cast<char> (p);
    printf("%d ", c);

    int* temp = reinterpret_cast<int *>(c);
    printf("%d ", *temp);

    return 0;
}

결과가 제대로 나오지 않을 뿐더러 중간에 프로그램이 터졌습니다.

그 이유는 char c = reinterpret_cast<char>(p); 부분 때문입니다.

여기서 c로 값이 전달 될 때 이미 받은 값은 변형이 일어나 '\0'이 됩니다.

또한 일반 변수가 포인터 성격을 띄우면서 받게되죠.

디 어셈블리로 보면 p가 가르키는 주소값이 들어가는 것이 아니라 '\0'이 들어가는 걸 확인할 수 있습니다.

그 수 int* temp = reinterpret_cast<int *>(c); 를 한다면 nullptr이 넘어가서 printf()에서 프로그램이 터지는 것입니다.

때문에 104라고 출력된 숫자도 정상적인 값 출력이 아닌 것입니다.


반대로 long과 unsigned int를 보겠습니다.



2) int * -> long or unsigned int -> int *

int main() {
    int* p = new int(100);
    long c = reinterpret_cast<long> (p);
    unsigned int i = reinterpret_cast<int>(p);
    printf("long c = %d, unsigned i = %d\n", c, i);

    int* temp1 = reinterpret_cast<int *>(c);
    int* temp2 = reinterpret_cast<int*>(i);
    printf("temp1 = %d, temp2 = %d\n", *temp1, *temp2);

    return 0;
}

주소값, 데이터가 잘 유지되면서 넘어간걸 확인할 수 있습니다.



3) int * -> char * -> int *

int main() {
    int* p = new int(100);
    char * c = reinterpret_cast<char *> (p);
    printf("*c = %d\n", *c);

    int* temp = reinterpret_cast<int *>(c);
    printf("*temp = %d\n", *temp);

    return 0;
}

위와 같은 경우는 잘 되는걸 확인 할 수 있습니다. 

char* 는 표현할 수 있는 수가 -128 ~ 127까지 이기 때문입니다.

또한 char*은 1바이트까지 표현할 수 있지만 포인터형이기 때문에 기본 자료형 크기는 4바이트 입니다.

따라서 주소값이 잘 유지되는걸 알 수 있습니다.

그럼 10000을 넣어보겠습니다.



int main() {
    int* p = new int(10000);
    char * c = reinterpret_cast<char *> (p);
    printf("*c = %d\n", *c);

    int* temp = reinterpret_cast<int *>(c);
    printf("*temp = %d\n", *temp);

    return 0;
}

값의 출력은 깨졌지만 원본 주소값은 온전히 int* temp로 넘어간걸 확인 할 수 있습니다.

때문에 포인터간의 reinterpret_cast는 출력하는 값은 깨져도 원본은 유지된다는걸 알 수 있습니다.



4) int * -> void * -> long *

int main() {
    int* p = new int(10000);
    void* v = (p);
    long* l = reinterpret_cast<long *>(v);
    printf("long* l = %d\n", *l);

    return 0;
}


int *p를 void *v로 받은 후 reinterpret_cast를 통해 long* 로 바꿔주고 있습니다.

잘 실행됩니다. reinterpret_cast를 사용하지 않고선 저런 방식이 불가능 하죠... void이기 때문에....








여러가지 예시를 나열해 봤는데..... 유익한 정보인지는 모르겠네요....

잘 사용하지도 않을 것 같고..... 흠....




반응형