이 RenterPret_cast가 컴파일하지 않는 이유는 무엇입니까?
-
18-09-2019 - |
문제
나는 이해 reinterpret_cast
위험합니다. 나는 단지 그것을 테스트하기 위해 이것을하고 있습니다. 다음 코드가 있습니다.
int x = 0;
double y = reinterpret_cast<double>(x);
프로그램을 컴파일하려고하면 오류가 발생합니다.
유형 'float'에서 type 'double에서 유효하지 않은 캐스트
무슨 일이야? 나는 생각했다 reinterpret_cast
사과를 잠수함으로 변환하는 데 사용할 수있는 도적 캐스트는 왜 간단한 캐스트 컴파일이 아닌가?
해결책
캐스트가 반환 한 값에 Y를 할당함으로써 실제로 값을 캐스팅하지 않습니다. x
, 당신은 그것을 변환하고 있습니다. 그건, y
지적하지 않습니다 x
그리고 그것이 부유물을 가리키는 척합니다. 변환은 새로운 유형의 값을 구성합니다 float
값을 할당합니다 x
. C ++ 에서이 변환을 수행하는 몇 가지 방법이 있습니다.
int main()
{
int x = 42;
float f = static_cast<float>(x);
float f2 = (float)x;
float f3 = float(x);
float f4 = x;
return 0;
}
유일한 차이는 마지막으로 (암시 적 변환) 더 높은 경고 수준에서 컴파일러 진단을 생성하는 것입니다. 그러나 그들은 모두 기능적으로 똑같은 일을합니다. 그리고 많은 경우 실제로 동일한 기계 코드에서와 같은 것.
이제 당신이 정말로 척하고 싶다면 x
플로트입니다. 그러면 당신은 정말로 캐스팅하고 싶어합니다 x
, 이렇게함으로써 :
#include <iostream>
using namespace std;
int main()
{
int x = 42;
float* pf = reinterpret_cast<float*>(&x);
(*pf)++;
cout << *pf;
return 0;
}
이것이 얼마나 위험한 지 알 수 있습니다. 사실, 내 컴퓨터에서 이것을 실행할 때의 출력은 1
, 이것은 확실히 42+1이 아닙니다.
다른 팁
C ++에서 reinterpret_cast
언어 사양에 명시 적으로 나열된 특정 변환 세트 만 수행 할 수 있습니다. 요컨대, reinterpret_cast
포인터-포인터 변환 및 참조-참조 전환 (포인터 투 인트거 및 정수-포인터 변환) 만 수행 할 수 있습니다. 이것은 캐스트의 이름으로 표현 된 의도와 일치합니다. 포인터/참조 재 해석에 사용되도록 의도됩니다.
당신이하려는 것은 재 해석이 아닙니다. an int
A로 double
참조 유형으로 변환해야합니다
double y = reinterpret_cast<double&>(x);
동등한 포인터 기반 재 해석은 아마도 더 명백 할 것입니다
double y = *reinterpret_cast<double*>(&x); // same as above
그러나 그 정도에 주목하십시오 reinterpret_cast
참조/포인터 유형을 변환 할 수 있습니다. 결과 참조/포인터를 통해 데이터를 읽으려는 실제 시도는 정의되지 않은 동작을 생성합니다.
그리고 어쨌든 이것은 물론 플랫폼에서 큰 의미가 없습니다. int
그리고 double
크기가 다릅니다 (더 큰 경우 double
당신은 점령 한 기억 너머를 읽습니다 x
).
그래서 결국 그것은 당신이 달성하려는 것들로 요약됩니다. 메모리 재 해석? 위 참조. 어떤 종류의 더 의미가 있습니다 int
에게 double
변환? 그렇다면, reinterpret_cast
여기서 당신을 도와주지 않을 것입니다.
reinterpret_cast는 일반적인 캐스트가 아닙니다. C ++ 03 사양에 따르면 섹션 5.2.10.1 :
ReneterPret_cast를 사용하여 명시 적으로 수행 할 수있는 변환은 아래에 나열되어 있습니다. ReneterPret_cast를 사용하여 명시 적으로 다른 변환을 수행 할 수 없습니다.
그리고 적분과 부동 소수점 유형 사이에서 변환하는 것을 설명하는 내용은 없습니다 (또는 적분 유형 사이에서도 불법입니다. reinterpret_cast<long>(int(3));
)
당신이 당신의 비트를 변환하려는 경우 int
A의 표현에 double
, 당신은 캐스팅해야합니다 주소 가치가 아닙니다. 또한 크기와 일치해야합니다.
uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
컴파일러는 넌센스로 쓴 내용을 거부합니다 int
그리고 double
크기가 다른 개체 일 수 있습니다. 확실히 위험하지만 이런 식으로 동일한 효과를 얻을 수 있습니다.
int x = 0;
double y = *reinterpret_cast<double*>(&x);
이는 경우에도 위험합니다 x
그리고 y
다른 크기입니다 (가정 해 봅시다 int
4 바이트이고 double
8 바이트입니다. &x
채우기 위해 y
4 바이트에 액세스합니다 x
그리고 4 바이트의 ... 다음에 기억에 나오는 것 (아마도 시작 y
, 또는 쓰레기 또는 다른 것들.)
정수를 더블로 변환하려면 static_cast
그리고 그것은 변환을 수행 할 것입니다.
비트 패턴에 액세스하려면 x
, 편리한 포인터 유형 (예 : byte*
) 및 접근 sizeof(int) / sizeof(byte)
:
byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
// do something with p[i]
}
RENETERPRET CAST를 사용하면 메모리 블록을 다른 유형으로 재 해석 할 수 있습니다. 이것은 포인터에서 수행되어야합니다 또는 참조:
int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f ); // !!
또 다른 것은 사실이 매우 위험한 캐스트라는 것입니다. 결과로 이상한 값이 나오거나 위의 주장이 실패하지 않을뿐만 아니라 유형이 크기가 다르고 '소스'에서 '소스'에서 재 해석되기 때문입니다. '대상'유형, 재 해석 된 참조/포인터의 모든 작업이 액세스됩니다. sizeof(destination)
바이트. 만약에 sizeof(destination)>sizeof(source)
그러면 실제 변수 메모리를 넘어서서 응용 프로그램을 죽이거나 소스 또는 대상 이외의 다른 변수를 덮어 쓰는 것입니다.
struct test {
int x;
int y;
};
test t = { 10, 20 };
double & d = reinterpret_cast<double&>( t.x );
d = 1.0/3.0;
assert( t.x != 10 ); // most probably at least.
asswet( t.y != 20 );
reinterpret_cast
포인터에 가장 잘 사용됩니다. 따라서 하나의 물체에 대한 포인터는 "잠수함"으로 바꿀 수 있습니다.
에서 MSDN:
RenterPret_cast 연산자는 본질적으로 안전하지 않은 char* to int* 또는 One_class*와 같은 전환에 사용할 수 있습니다.
RenterPret_cast의 결과는 원래 유형으로 다시 캐스트되는 것 외에는 안전하게 사용할 수 없습니다. 다른 용도는 기껏해야 할 수 없습니다.
재 해석 접근 방식은 불완전한 결과로 이상한 경로를 이끌었습니다. 결국 나는 이렇게 memcpy가 훨씬 낫다는 것을 알았습니다!
double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
int를 더블로 캐스트하는 것은 캐스트가 필요하지 않습니다. 컴파일러는 암시 적으로 할당을 수행합니다.
reinterpret_cast는 포인터와 참조와 함께 사용됩니다. int *
a double *
.
그 흥미 롭군요. 어쩌면 캐스트가 두 배로 시도되기 전에 Int에서 Float로 암시적인 변환을 수행 할 수 있습니다. INT 및 플로트 유형은 바이트에서 크기가 같은 경향이 있습니다 (물론 시스템에 따라 다름).