Namespaces
Variants

reinterpret_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

기본 비트 패턴을 재해석하여 타입 간 변환을 수행합니다.

목차

구문

reinterpret_cast< 대상-유형 >( 표현식 )

target-type 타입의 값을 반환합니다.

설명

static_cast 와는 달리, 그러나 const_cast 와 마찬가지로, reinterpret_cast 표현식은 어떤 CPU 명령어로도 컴파일되지 않습니다 (정수와 포인터 간 변환, 또는 포인터 표현이 타입에 따라 달라지는 특수 아키텍처에서 포인터 간 변환인 경우 제외). 이는 주로 컴파일 타임 지시자로서, 컴파일러가 expression 을 마치 target-type 타입을 가진 것처럼 처리하도록 지시합니다.

다음 변환만이 reinterpret_cast 로 수행될 수 있으며, 이러한 변환이 constness 제거 (또는 volatility 제거)를 유발하는 경우는 예외입니다.

1) 정수형, 열거형, 포인터, 또는 멤버-포인터 타입의 표현식을 해당 타입 자체로 변환할 수 있습니다. 결과 값은 expression 의 값과 동일합니다.
2) 포인터는 해당 타입의 모든 값을 담을 수 있을 만큼 충분히 큰 임의의 정수 타입으로 변환될 수 있습니다(예: std::uintptr_t ).
3) 모든 정수형 또는 열거형 타입의 값은 포인터 타입으로 변환될 수 있습니다. 충분한 크기의 정수로 변환된 포인터가 다시 동일한 포인터 타입으로 변환될 경우 원래 값을 보장받지만, 그렇지 않을 경우 결과 포인터를 안전하게 역참조할 수 없습니다(반대 방향의 왕복 변환은 보장되지 않으며, 동일한 포인터가 여러 정수 표현을 가질 수 있습니다). 널 포인터 상수 NULL 또는 정수 0이 대상 타입의 널 포인터 값을 생성한다는 보장은 없습니다. 이 목적을 위해서는 static_cast 또는 implicit conversion 을 사용해야 합니다.
4) std::nullptr_t 타입의 모든 값은 nullptr 을 포함하여 ( void * ) 0 인 것처럼 모든 정수형 타입으로 변환될 수 있습니다. 그러나 어떤 값도, 심지어 nullptr 조차도 std::nullptr_t 로 변환될 수 없습니다: 이 목적을 위해서는 static_cast 을 사용해야 합니다.
(C++11부터)
5) 모든 객체 포인터 타입 T1* 는 다른 객체 포인터 타입 cv T2* 로 변환될 수 있습니다. 이는 다음 코드와 완전히 동등합니다: static_cast < cv T2 * > ( static_cast < cv void * > ( expression ) ) (이는 만약 T2 의 정렬 요구사항이 T1 의 정렬 요구사항보다 엄격하지 않다면, 포인터 값이 변경되지 않고 결과 포인터를 원래 타입으로 다시 변환하면 원래 값을 얻는다는 것을 의미합니다). 어떤 경우든, 결과 포인터는 역참조된 값이 타입 접근 가능 할 때만 안전하게 역참조될 수 있습니다.
6) T1 타입의 lvalue (C++11 이전) glvalue (C++11 이후) 표현식은 다른 타입 T2 에 대한 참조로 변환될 수 있습니다. 결과는 * reinterpret_cast < T2 * > ( p ) 의 결과와 동일하며, 여기서 p 표현식 이 지정하는 객체나 함수를 가리키는 "pointer to T1 " 타입의 포인터입니다. 임시 객체가 구체화되거나 생성되지 않으며 (C++17 이후) , 복사본이 생성되지 않고, 생성자나 변환 함수가 호출되지 않습니다. 결과 참조는 타입 접근 가능 한 경우에만 안전하게 접근할 수 있습니다.
7) 함수에 대한 모든 포인터는 다른 함수 타입에 대한 포인터로 변환될 수 있습니다. 결과는 명시되지 않지만, 이러한 포인터를 원본 함수 타입에 대한 포인터로 다시 변환하면 원본 함수에 대한 포인터를 얻습니다. 결과 포인터는 함수 타입이 원본 함수 타입과 호환 가능(call-compatible) 한 경우에만 안전하게 호출할 수 있습니다.
8) 일부 구현(특히 POSIX 호환 시스템에서 dlsym 에 의해 요구되는 대로)에서는 함수 포인터를 void * 또는 다른 객체 포인터로 변환하거나 그 반대로 변환할 수 있습니다. 구현체가 양방향 변환을 지원하는 경우, 원본 타입으로 변환하면 원본 값을 얻지만, 그렇지 않으면 결과 포인터를 역참조하거나 안전하게 호출할 수 없습니다.
9) 모든 포인터 타입의 널 포인터 값은 다른 어떤 포인터 타입으로도 변환될 수 있으며, 그 결과는 해당 타입의 널 포인터 값이 됩니다. 널 포인터 상수 nullptr 또는 std::nullptr_t 타입의 다른 어떤 값도 reinterpret_cast 를 사용하여 포인터로 변환할 수 없습니다: 이 목적에는 암시적 변환 또는 static_cast 를 사용해야 합니다.
10) 멤버 함수에 대한 포인터는 다른 타입의 다른 멤버 함수에 대한 포인터로 변환될 수 있습니다. 원본 타입으로 다시 변환하면 원래 값을 얻지만, 그렇지 않을 경우 결과 포인터는 안전하게 사용할 수 없습니다.
11) 어떤 클래스 T1 의 멤버 객체에 대한 포인터는 다른 클래스 T2 의 다른 멤버 객체에 대한 포인터로 변환될 수 있습니다. T2 의 정렬 요구사항이 T1 의 정렬 요구사항보다 엄격하지 않은 경우, 원래 타입 T1 으로 다시 변환하면 원래 값을 얻을 수 있습니다. 그렇지 않으면 결과 포인터를 안전하게 사용할 수 없습니다.

모든 형변환 표현식과 마찬가지로, 결과는 다음과 같습니다:

  • target-type 이 lvalue 참조 타입인 경우 lvalue 또는 함수 타입에 대한 rvalue 참조인 경우 (C++11부터) ;
  • target-type 가 객체 타입에 대한 rvalue 참조인 경우 xvalue;
(since C++11)
  • 그렇지 않으면 prvalue입니다.

타입 별칭

타입 접근성

만약 타입 T_ref 가 다음 타입들 중 하나와 유사(similar) 하다면, 동적 타입(dynamic type) T_obj 인 객체는 T_ref 타입의 lvalue (C++11까지) glvalue (C++11부터) 를 통해 타입 접근 가능(type-accessible) 합니다:

  • char , unsigned char 또는 std::byte (C++17 이후) : 이를 통해 모든 객체의 객체 표현 을 바이트 배열로 검사할 수 있습니다.
  • T_obj
  • T_obj 에 해당하는 부호 있는 또는 부호 없는 타입

프로그램이 형식 접근이 불가능한 lvalue (until C++11) glvalue (since C++11) 를 통해 객체의 저장된 값을 읽거나 수정하려고 시도하는 경우, 그 동작은 정의되지 않습니다.

이 규칙은 타입 기반 별칭 분석을 활성화합니다. 여기서 컴파일러는 한 타입의 glvalue를 통해 읽은 값이 다른 타입의 glvalue에 대한 쓰기로 수정되지 않는다고 가정합니다(위에서 언급한 예외 사항 적용).

많은 C++ 컴파일러가 비표준 언어 확장으로 이 규칙을 완화하여, union 의 비활성 멤버를 통해 잘못된 타입으로의 접근을 허용한다는 점에 유의하십시오 (이러한 접근은 C에서는 undefined behavior가 아닙니다).

호환성 호출

다음 조건 중 하나라도 충족되면, 타입 T_call 는 함수 타입 T_func 호환 가능(call-compatible) 합니다:

  • T_call T_func 와 동일한 타입입니다.
(C++17부터)

함수가 호출될 때 표현식의 function type 이 호출된 함수 정의의 타입과 호환되지 않는 경우, 그 동작은 정의되지 않습니다.

참고 사항

정렬 요구사항이 충족된다고 가정할 때, reinterpret_cast 포인터의 값 을 변경하지 않습니다. 단, pointer-interconvertible 객체를 다루는 몇 가지 제한된 경우는 예외입니다:

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // 표준 레이아웃이 아님
union U { int a; double b; } u = {0};
int arr[2];
int* p1 = reinterpret_cast<int*>(&s1); // p1의 값은 "s1.a를 가리키는 포인터"입니다.
                                       // s1.a와 s1은 포인터 상호 변환 가능하기 때문입니다
int* p2 = reinterpret_cast<int*>(&s2); // p2의 값은 reinterpret_cast에 의해 변경되지 않으며
                                       // "s2를 가리키는 포인터"입니다
int* p3 = reinterpret_cast<int*>(&u);  // p3의 값은 "u.a를 가리키는 포인터"입니다:
                                       // u.a와 u는 포인터 상호 변환 가능합니다
double* p4 = reinterpret_cast<double*>(p3); // p4의 값은 "u.b를 가리키는 포인터"입니다: u.a와
                                            // u.b는 포인터 상호 변환 가능합니다. 왜냐하면
                                            // 둘 다 u와 포인터 상호 변환 가능하기 때문입니다
int* p5 = reinterpret_cast<int*>(&arr); // p5의 값은 reinterpret_cast에 의해 변경되지 않으며
                                        // "arr을 가리키는 포인터"입니다

적절한 타입의 객체를 실제로 지정하지 않는 glvalue(예: reinterpret_cast 를 통해 얻은 것)에 대해 비정적 데이터 멤버 또는 비정적 멤버 함수를 지정하는 클래스 멤버 접근을 수행하는 것은 정의되지 않은 행동을 초래합니다:

struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {};    // 표준 레이아웃
struct ST : S, T {}; // 표준 레이아웃 아님
S s = {};
auto p = reinterpret_cast<T*>(&s); // p의 값은 "s를 가리키는 포인터"
auto i = p->x; // 클래스 멤버 접근 표현식은 정의되지 않은 동작;
               // s는 T 객체가 아님
p->x = 1; // 정의되지 않은 동작
p->f();   // 정의되지 않은 동작
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // p1의 값은 "s1의 S 하위 객체를 가리키는 포인터"
auto i = p1->x; // 정상
p1->x = 1;      // 정상
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // p2의 값은 "st를 가리키는 포인터"
auto i = p2->x; // 정의되지 않은 동작
p2->x = 1;      // 정의되지 않은 동작

많은 컴파일러가 이러한 경우 "strict aliasing" 경고를 발생시키는데, 비록 기술적으로 그러한 구조가 일반적으로 "strict aliasing rule"로 알려진 문단보다 다른 것에 위배되더라도 그렇습니다.

strict aliasing 및 관련 규칙의 목적은 타입 기반 별칭 분석을 가능하게 하는 것입니다. 프로그램이 서로 관련 없는 타입에 대한 두 개의 포인터(예: int * float * )가 동시에 존재하고 둘 다 동일한 메모리를 로드하거나 저장하는 데 사용될 수 있는 상황을 유효하게 생성할 수 있다면 이 분석이 크게 약화될 것입니다( SG12 리플렉터의 이 이메일 참조). 따라서 그러한 상황을 생성할 수 있는 것으로 보이는 어떤 기법도 필연적으로 미정의 동작을 유발합니다.

객체의 바이트를 다른 타입의 값으로 해석해야 할 때, std::memcpy 또는 std::bit_cast (C++20부터) 를 사용할 수 있습니다:

double d = 0.1;
std::int64_t n;
static_assert(sizeof n == sizeof d);
// n = *reinterpret_cast<std::int64_t*>(&d); // 정의되지 않은 동작
std::memcpy(&n, &d, sizeof d);               // 정상
n = std::bit_cast<std::int64_t>(d);          // 또한 정상

구현체가 std::intptr_t 및/또는 std::uintptr_t 를 제공하는 경우, 객체 타입이나 cv void 에 대한 포인터를 이러한 타입으로의 캐스트는 항상 잘 정의됩니다. 그러나 함수 포인터에 대해서는 이것이 보장되지 않습니다.

(C++11부터)

C에서는 집합체 복사와 할당이 집합체 객체 전체에 접근합니다. 그러나 C++에서는 이러한 동작이 항상 멤버 함수 호출을 통해 수행되며, 이는 전체 객체가 아닌 개별 하위 객체에 접근합니다(또는 공용체의 경우 객체 표현을 복사합니다, 즉 unsigned char 를 통해).

키워드

reinterpret_cast

예제

reinterpret_cast 의 몇 가지 사용 예시를 보여줍니다:

#include <cassert>
#include <cstdint>
#include <iostream>
int f() { return 42; }
int main()
{
    int i = 7;
    // 포인터를 정수로 변환하고 다시 되돌리기
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast는 오류 발생
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
    // 함수 포인터를 다른 함수 포인터로 변환하고 다시 되돌리기
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); 정의되지 않은 동작
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // 안전함
    // 포인터를 통한 타입 앨리어싱
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
    // 참조를 통한 타입 앨리어싱
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // 컴파일러 오류 - const 제거 불가
    // const_cast를 사용해야 함: int &iref = const_cast<int&>(const_iref);
}

가능한 출력:

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 195 C++98 함수 포인터와 객체 포인터 간 변환
허용되지 않음
조건부 지원으로 변경
CWG 658 C++98 포인터 변환 결과가 명시되지 않음
(원본 타입으로의 역변환 제외)
가리키는 타입이 정렬 요구사항을
만족하는 포인터에 대한 명세 제공
CWG 799 C++98 어떤 항등 변환이
reinterpret_cast 로 수행 가능한지 불명확
명확히 규정
CWG 1268 C++11 reinterpret_cast 가 참조 타입으로
lvalue만 캐스트 가능
xvalue도 허용
CWG 2780 C++98 reinterpret_cast 가 함수 lvalue를
다른 참조 타입으로 캐스트 불가
허용
CWG 2939 C++17 reinterpret_cast 가 prvalue를
rvalue 참조 타입으로 캐스트 가능
허용되지 않음

참고문헌

  • C++23 표준 (ISO/IEC 14882:2024):
  • 7.6.1.10 Reinterpret cast [expr.reinterpret.cast]
  • C++20 표준(ISO/IEC 14882:2020):
  • 7.6.1.9 재해석 변환 [expr.reinterpret.cast]
  • C++17 표준 (ISO/IEC 14882:2017):
  • 8.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++14 표준(ISO/IEC 14882:2014):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++11 표준 (ISO/IEC 14882:2011):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++98 표준(ISO/IEC 14882:1998):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++03 표준(ISO/IEC 14882:2003):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]

참고 항목

const_cast conversion const 추가 또는 제거
static_cast conversion 기본 변환 수행
dynamic_cast conversion 검증된 다형성 변환 수행
명시적 캐스트 타입 간 허용적 변환
표준 변환 한 타입에서 다른 타입으로의 암시적 변환
(C++20)
한 타입의 객체 표현을 다른 타입의 표현으로 재해석
(함수 템플릿)