Programing

reinterpret_cast는 언제 사용합니까?

lottogame 2020. 2. 19. 21:19
반응형

reinterpret_cast는 언제 사용합니까?


reinterpret_castvs 의 적용 가능성과 혼동되지 않습니다 static_cast. 내가 읽은 일반적인 규칙에서 컴파일 타임에 유형을 해석 할 수있을 때 정적 캐스트를 사용하는 것이 단어 static입니다. 이것은 C ++ 컴파일러가 암시 적 캐스트에도 내부적으로 사용하는 캐스트입니다.

reinterpret_cast두 가지 시나리오에 적용 할 수 있습니다.

  • 정수 유형을 포인터 유형으로 변환하거나 그 반대로 변환
  • 한 포인터 유형을 다른 포인터 유형으로 변환하십시오. 내가 얻는 일반적인 아이디어는 이것이 이식 불가능하며 피해야한다는 것입니다.

내가 약간 혼란스러워하는 곳에서 필요한 사용법이 하나 있는데, C에서 C ++을 호출하고 C 코드는 C ++ 객체를 잡아야하므로 기본적으로 void*. void *클래스 유형과 클래스 유형 을 변환하려면 어떤 캐스트를 사용해야 합니까?

난 둘의 사용 본 static_cast과를 reinterpret_cast? 내가 읽은 것에서 static컴파일 타임에 캐스트가 발생할 수 있기 때문에 더 나은 것처럼 보입니까? reinterpret_cast한 포인터 유형에서 다른 포인터 유형으로 변환하는 데 사용한다고 말하고 있습니까?


C ++ 표준은 다음을 보장합니다.

static_castvoid*주소 유지하려면 주소 유지하십시오. 즉, 다음에,이다 a, b그리고 c같은 주소에 대한 모든 점 :

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_cast다른 유형으로 포인터를 캐스트 한 다음 reinterpret_cast원래 유형으로 다시 되 돌리면 원래 값을 얻습니다. 따라서 다음에서 :

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

ac같은 값을 포함하지만,의 값은 b지정되지 않습니다. (실제로는 일반적으로 같은 주소를 포함 a하고 c있지만, 표준에 명시되지 않은 것, 그리고 더 복잡한 메모리 시스템과 시스템에 진실하지 않을 수 있습니다.)

과에서 주조를 들어 void*, static_cast선호한다.


reinterpret_cast불투명 한 데이터 유형과 인터페이스 할 때 필요한 경우 가 있습니다. 프로그래머가 제어 할 수없는 공급 업체 API에서 자주 발생합니다. 다음은 공급 업체가 임의의 전역 데이터를 저장하고 검색하기위한 API를 제공하는 고안된 예입니다.

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

이 API를 사용하려면 프로그래머가 데이터를 VendorGlobalUserData다시 캐스팅해야 합니다. static_cast작동하지 않으면 reinterpret_cast다음을 사용해야합니다 .

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

다음은 샘플 API를 구현 한 것입니다.

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }

짧은 대답 : 당신이 무엇을 reinterpret_cast의미 하는지 모른다면 그것을 사용하지 마십시오. 앞으로 필요할 경우 알게 될 것입니다.

전체 답변 :

기본 숫자 유형을 고려해 봅시다.

int(12)들어 unsigned float (12.0f)프로세서 로 변환 할 때 두 숫자의 비트 표현이 다르기 때문에 일부 계산을 호출해야합니다. 이것이 static_cast의미 하는 바입니다.

반면에 reinterpret_castCPU를 호출하면 계산이 호출되지 않습니다. 다른 유형의 것처럼 메모리의 비트 세트를 처리합니다. 당신이 변환 할 때 그래서 int*float*이 키워드와 함께, (포인터 dereferecing 후) 새 값은 수학적 의미에서 이전 값과는 아무 상관이 없습니다.

예 :reinterpret_cast 바이트 순서 (엔디안)라는 한 가지 이유로 인해 이식성이없는것은 사실입니다. 그러나 이것은 종종 놀랍게도 그것을 사용하는 가장 좋은 이유입니다. 예제를 상상해 봅시다 : 파일에서 이진 32 비트 숫자를 읽어야하며, 그것이 빅 엔디안이라는 것을 알고 있습니다. 코드는 일반적이어야하며 빅 엔디안 (예 : 일부 ARM) 및 리틀 엔디안 (예 : x86) 시스템에서 제대로 작동합니다. 따라서 바이트 순서를 확인해야합니다. 당신이 쓸 수 있도록 컴파일 시간에 잘 알려진 constexpr기능 : 당신은이를 달성하기 위해 함수를 작성할 수 있습니다 :

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

설명 :x 메모리에서이진 표현은0000'0000'0000'0001(큰) 또는0000'0001'0000'0000(little 엔디안) 일 수 있습니다. 재 해석 후p포인터아래의 바이트는각각0000'0000또는0000'0001입니다. 정적 캐스팅을 사용하는 경우0000'0001어떤 엔디안 (endianness)을 사용하든항상그렇습니다.

편집하다:

첫 번째 버전에서는 예제 함수 is_little_endian를로 만들었습니다 constexpr. 최신 gcc (8.3.0)에서 잘 컴파일되지만 표준에 따르면 불법입니다. clang 컴파일러는 컴파일을 거부합니다 (정확합니다).


의 의미 reinterpret_cast는 C ++ 표준에 의해 정의되지 않습니다. 따라서 이론적으로 reinterpret_cast는 프로그램을 중단시킬 수 있습니다. 실제로 컴파일러는 예상 한 것을 수행하려고 시도합니다. 즉, 전달하는 비트를 마치 캐스팅하려는 유형 인 것처럼 해석합니다. 사용할 컴파일러가 무엇인지 알고 있다면 reinterpret_cast그것을 사용할 수 있지만 이식성있다고 말하는 것은 거짓말 일 것입니다.

당신이 묘사하는 경우와 고려할 수있는 거의 모든 경우 대신에 다른 대안을 reinterpret_cast사용할 수 있습니다 static_cast. 무엇보다도 표준은 기대할 수있는 것에 대해 말하고 있습니다 static_cast(§5.2.9).

“pointer to cv void”유형의 rvalue는 객체 유형에 대한 포인터로 명시 적으로 변환 될 수 있습니다. "포인터에서 cv void 로의 포인터"로 변환 된 객체에 대한 포인터 유형의 값은 원래 포인터 유형으로 돌아갑니다.

따라서 유스 케이스의 경우 표준화위원회가 귀하가 사용하도록 의도 한 것이 분명해 보입니다 static_cast.


reinterpret_cast의 한 가지 사용법은 비트 연산을 (IEEE 754) 플로트에 적용하려는 경우입니다. 이에 대한 한 가지 예는 Fast Inverse Square-Root 트릭입니다.

https://ko.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

float의 이진 표현을 정수로 취급하고 오른쪽으로 이동하여 상수에서 빼서 지수를 반으로 줄입니다. 다시 float로 변환 한 후에는 근사값을보다 정확하게하기 위해 Newton-Raphson 반복이 적용됩니다.

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

원래 C로 작성되었으므로 C 캐스트를 사용하지만 유사한 C ++ 캐스트는 reinterpret_cast입니다.


컴파일시 reinterprete_cast를 사용하여 상속을 확인할 수 있습니다.
여기를보십시오 : 컴파일 타임에 상속을 확인하기 위해 reinterpret_cast 사용


template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

템플릿을 사용하여 간단한 안전한 캐스트를 작성하고 작성했습니다. 이 솔루션은 함수에 대한 포인터 캐스트를 보장하지 않습니다.


먼저 int와 같은 특정 유형의 데이터가 있습니다.

int x = 0x7fffffff://==nan in binary representation

그런 다음 float과 같은 다른 유형과 동일한 변수에 액세스하려고합니다.

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

또는

float y = *(float*)&(x);

//this could be used in c and cpp

요약 : 동일한 메모리가 다른 유형으로 사용됨을 의미합니다. 따라서 위와 같은 int 유형으로 float의 이진 표현을 float로 변환 할 수 있습니다. 예를 들어 0x80000000은 -0입니다 (가수와 지수는 null이지만 부호 msb는 1입니다). 이것은 또한 double과 long double에도 적용됩니다.

최적화 : reinterpret_cast는 많은 컴파일러에서 최적화 될 것이라고 생각하지만 c- 캐스팅은 포인터 산술로 수행됩니다 (값은 메모리에 복사되어야하며 포인터는 cpu 레지스터를 가리킬 수 없습니다).

참고 : 두 경우 모두 캐스팅 전에 캐스팅 된 값을 변수에 저장해야합니다! 이 매크로가 도움이 될 수 있습니다.

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })

사용하는 한 가지 이유 reinterpret_cast는 기본 클래스에 vtable이 없지만 파생 클래스가있는 경우입니다. 이 경우, static_castreinterpret_cast(이 언급 비정형 될 경우 다른 포인터 값을 초래할 것이다 전술 jalf ). 면책 조항처럼, 이것이 표준의 일부가 아니라 여러 널리 퍼진 컴파일러의 구현이라고 진술하고 있습니다.

예를 들어 아래 코드를 사용하십시오.

#include <cstdio>

class A {
public:
    int i;
};

class B : public A {
public:
    virtual void func() {  }
};

int main()
{
    B b;
    const A* a = static_cast<A*>(&b);
    const A* ar = reinterpret_cast<A*>(&b);

    printf("&b = %p\n", &b);
    printf(" a = %p\n", a);
    printf("ar = %p\n", ar);
    printf("difference = %ld\n", (long int)(a - ar));

    return 0;
}

다음과 같은 결과가 나옵니다.

& b = 0x7ffe10e68b38
a = 0x7ffe10e68b40
ar = 0x7ffe10e68b38
차이 = 2

내가 시도한 모든 컴파일러 (MSVC 2015 및 2017, clang 8.0.0, gcc 9.2, icc 19.0.1- 마지막 3godbolt 참조 )에서 결과는 2 의 결과 static_cast와 다릅니다 reinterpret_cast(MSVC의 경우 4). 차이점에 대해 경고하는 유일한 컴파일러는 다음과 같이 clang이었습니다.

17:16 : 경고 : 'B *'클래스에서 '0이 아닌 오프셋에있는 기본으로'reinterpret_cast ''A * '는'static_cast '와 다르게 동작합니다. [-Wreinterpret-base-class]
const A * ar = reinterpret_cast (& b) ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
17:16 : 참고 : 'static_cast'를 사용하여 업 캐스팅하는 동안 포인터를 올바르게 조정하십시오.
const A * ar = reinterpret_cast (& b) ;
^ ~~~~~~~~~~~~~~~
static_cast

마지막으로주의해야 할 점은 기본 클래스는 데이터 멤버가없는 경우 (예를 들어 있다는 것입니다 int i;) 다음 그 소리, GCC 및 ICC가 같은 주소를 반환 reinterpret_cast에 관해서는 static_castMSVC 여전히하지 않는 반면,.


빠른 답변 : static_cast컴파일하면 사용 하고 그렇지 않으면를 사용 하십시오 reinterpret_cast.


FAQ를 읽으십시오 ! C에서 C ++ 데이터를 보유하는 것은 위험 할 수 있습니다.

C ++에서 void *캐스트없이 객체에 대한 포인터를 변환 할 수 있습니다 . 그러나 다른 방법으로는 사실이 아닙니다. 당신은 필요한 것 static_cast원래의 포인터 등을 얻을 수 있습니다.

참고 URL : https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast



반응형