reinterpret_cast는 언제 사용합니까?
reinterpret_cast
vs 의 적용 가능성과 혼동되지 않습니다 static_cast
. 내가 읽은 일반적인 규칙에서 컴파일 타임에 유형을 해석 할 수있을 때 정적 캐스트를 사용하는 것이 단어 static
입니다. 이것은 C ++ 컴파일러가 암시 적 캐스트에도 내부적으로 사용하는 캐스트입니다.
reinterpret_cast
두 가지 시나리오에 적용 할 수 있습니다.
- 정수 유형을 포인터 유형으로 변환하거나 그 반대로 변환
- 한 포인터 유형을 다른 포인터 유형으로 변환하십시오. 내가 얻는 일반적인 아이디어는 이것이 이식 불가능하며 피해야한다는 것입니다.
내가 약간 혼란스러워하는 곳에서 필요한 사용법이 하나 있는데, C에서 C ++을 호출하고 C 코드는 C ++ 객체를 잡아야하므로 기본적으로 void*
. void *
클래스 유형과 클래스 유형 을 변환하려면 어떤 캐스트를 사용해야 합니까?
난 둘의 사용 본 static_cast
과를 reinterpret_cast
? 내가 읽은 것에서 static
컴파일 타임에 캐스트가 발생할 수 있기 때문에 더 나은 것처럼 보입니까? reinterpret_cast
한 포인터 유형에서 다른 포인터 유형으로 변환하는 데 사용한다고 말하고 있습니까?
C ++ 표준은 다음을 보장합니다.
static_cast
void*
주소 를 유지하려면 주소 를 유지하십시오. 즉, 다음에,이다 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);
a
와 c
같은 값을 포함하지만,의 값은 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_cast
CPU를 호출하면 계산이 호출되지 않습니다. 다른 유형의 것처럼 메모리의 비트 세트를 처리합니다. 당신이 변환 할 때 그래서 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_cast
및 reinterpret_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- 마지막 3 은 godbolt 참조 )에서 결과는 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_cast
MSVC 여전히하지 않는 반면,.
빠른 답변 : static_cast
컴파일하면 사용 하고 그렇지 않으면를 사용 하십시오 reinterpret_cast
.
FAQ를 읽으십시오 ! C에서 C ++ 데이터를 보유하는 것은 위험 할 수 있습니다.
C ++에서 void *
캐스트없이 객체에 대한 포인터를 변환 할 수 있습니다 . 그러나 다른 방법으로는 사실이 아닙니다. 당신은 필요한 것 static_cast
원래의 포인터 등을 얻을 수 있습니다.
참고 URL : https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast
'Programing' 카테고리의 다른 글
마스터에서 개발 브랜치로 git pull (0) | 2020.02.19 |
---|---|
'화살표 함수'와 '함수'는 동일하거나 교환 가능합니까? (0) | 2020.02.19 |
Bootstrap 3.x에서 사용되는 .map 파일은 무엇입니까? (0) | 2020.02.19 |
Machine.Config는 어디에 있습니까? (0) | 2020.02.19 |
추상 클래스를 단위 테스트하는 방법 : 스텁으로 확장? (0) | 2020.02.19 |