Khi nào nên sử dụng reinterpret_cast?


459

Tôi ít nhầm lẫn với phạm vi áp dụng của reinterpret_castvs static_cast. Từ những gì tôi đã đọc các quy tắc chung là sử dụng truyền tĩnh khi các loại có thể được giải thích tại thời điểm biên dịch do đó từstatic . Đây là diễn viên mà trình biên dịch C ++ sử dụng nội bộ cho các diễn viên ngầm.

reinterpret_casts được áp dụng trong hai kịch bản:

  • chuyển đổi các kiểu số nguyên thành các loại con trỏ và ngược lại
  • chuyển đổi một loại con trỏ sang loại khác. Ý tưởng chung tôi nhận được là điều này là không thể tin được và nên tránh.

Trong đó tôi có một chút bối rối là một cách sử dụng mà tôi cần, tôi đang gọi C ++ từ C và mã C cần phải giữ đối tượng C ++ để về cơ bản nó giữ một void*. Diễn viên nào nên được sử dụng để chuyển đổi giữavoid * và loại Class?

Tôi đã thấy việc sử dụng của cả hai static_castreinterpret_cast? Mặc dù từ những gì tôi đã đọc, nó xuất hiện statictốt hơn vì dàn diễn viên có thể xảy ra vào thời gian biên dịch? Mặc dù nó nói để sử dụng reinterpret_castđể chuyển đổi từ một loại con trỏ sang loại khác?


9
reinterpret_castkhông xảy ra trong thời gian chạy. Cả hai đều là các báo cáo thời gian biên dịch. Từ en.cppreference.com/w/cpp/lingu/reinterpret_cast : "Không giống như static_cast, nhưng giống như const_cast, biểu thức reinterpret_cast không biên dịch theo bất kỳ lệnh CPU nào. (biểu diễn đối tượng) của biểu thức như thể nó có kiểu new_type. "
Cris Luengo ngày

@HeretoLearn, có thể thêm các đoạn mã có liên quan từ tệp * .c và * .cpp không? Tôi nghĩ rằng nó có thể cải thiện việc giải thích câu hỏi.
OrenIshShalom

Câu trả lời:


442

Tiêu chuẩn C ++ đảm bảo những điều sau:

static_casting một con trỏ đến và từ void*bảo tồn địa chỉ. Đó là, sau đây a, bctất cả chỉ đến cùng một địa chỉ:

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

reinterpret_castchỉ đảm bảo rằng nếu bạn chuyển một con trỏ sang một loại khác, và sau đó reinterpret_castnó trở về loại ban đầu , bạn sẽ nhận được giá trị ban đầu. Vì vậy, sau đây:

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

acchứa cùng một giá trị, nhưng giá trị của blà không xác định. (trong thực tế, nó thường chứa cùng một địa chỉ ac , nhưng điều đó không được chỉ định trong tiêu chuẩn và nó có thể không đúng trên các máy có hệ thống bộ nhớ phức tạp hơn.)

Để đúc đến và đi void*, static_castnên được ưu tiên.


18
Tôi thích thực tế là 'b' không xác định. Nó ngăn bạn làm những điều ngớ ngẩn với nó. Nếu bạn chuyển một cái gì đó sang một loại con trỏ khác, bạn sẽ yêu cầu các vấn đề và thực tế là bạn không thể phụ thuộc vào nó khiến bạn cẩn thận hơn. Nếu bạn đã sử dụng static_cast <> thì sử dụng 'b' là gì?
Martin York

3
Tôi nghĩ rằng reinterpret_cast <> đảm bảo cùng một mẫu bit. (không giống như một con trỏ hợp lệ cho loại khác).
Martin York

37
giá trị của bkhông còn là không xác định nữa trong C ++ 11 khi sử dụng reinterpret_cast. Và trong C ++ 03, một loạt các int*lệnh void*cấm đã bị cấm thực hiện reinterpret_cast(mặc dù trình biên dịch không thực hiện điều đó và nó không thực tế, do đó đã được thay đổi cho C ++ 11).
Julian Schaub - litb

55
Điều này không thực sự trả lời câu hỏi "khi nào nên sử dụng reinterpret_cast".
einpoklum

6
@LokiAstari Tôi nghĩ rằng không xác định không ngăn bạn làm những điều ngớ ngẩn. Nó chỉ dừng lại khi bạn nhớ nó không xác định. Sự khác biệt lớn. Cá nhân tôi không thích không xác định. Quá nhiều thứ để nhớ
Helin Wang

158

Một trường hợp khi reinterpret_castcần thiết là khi giao tiếp với các loại dữ liệu mờ. Điều này xảy ra thường xuyên trong các API của nhà cung cấp mà lập trình viên không kiểm soát được. Đây là một ví dụ giả định trong đó nhà cung cấp cung cấp API để lưu trữ và truy xuất dữ liệu toàn cầu tùy ý:

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

Để sử dụng API này, lập trình viên phải truyền dữ liệu của họ tới VendorGlobalUserDatavà lại. static_castsẽ không hoạt động, người ta phải sử dụng 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;
}

Dưới đây là triển khai API mẫu:

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

7
Đúng, đó là về cách sử dụng reinterpret_cast có ý nghĩa duy nhất mà tôi có thể nghĩ ra.
jalf

8
Đây có thể là một câu hỏi muộn, nhưng tại sao API của nhà cung cấp không sử dụng void*cho điều đó?
Xèo

19
@Xeo Họ không sử dụng void * vì sau đó họ mất (một số) kiểm tra loại tại thời điểm biên dịch.
jesup

4
Trường hợp sử dụng thực tế của các loại dữ liệu "mờ" là khi bạn muốn hiển thị API cho C nhưng viết triển khai trong C ++. ICU là một ví dụ về một thư viện thực hiện việc này ở một số nơi. Ví dụ: trong API trình kiểm tra giả mạo, bạn xử lý các con trỏ loại USpoofChecker*, trong đó USpoofCheckercó cấu trúc trống. Tuy nhiên, dưới mui xe, bất cứ khi nào bạn vượt qua a USpoofChecker*, nó sẽ trải qua reinterpret_castloại C ++ bên trong.
sffc

@sffc tại sao không để lộ kiểu cấu trúc C cho người dùng?
Gupta

101

Câu trả lời ngắn gọn: Nếu bạn không biết gìreinterpret_cast đại diện cho, đừng sử dụng nó. Nếu bạn sẽ cần nó trong tương lai, bạn sẽ biết.

Câu trả lời đầy đủ:

Hãy xem xét các loại số cơ bản.

Khi bạn chuyển đổi ví dụ int(12)sang unsigned float (12.0f)bộ xử lý của bạn cần phải gọi một số phép tính vì cả hai số có biểu diễn bit khác nhau. Đây là những gìstatic_cast đại diện cho.

Mặt khác, khi bạn gọi reinterpret_castCPU không gọi bất kỳ phép tính nào. Nó chỉ xử lý một tập hợp các bit trong bộ nhớ như thể nó có loại khác. Vì vậy, khi bạn chuyển đổi int*sang float*từ khóa này, giá trị mới (sau khi hủy bỏ con trỏ) không liên quan gì đến giá trị cũ theo nghĩa toán học.

Ví dụ: Đúngreinterpret_castlà không khả chuyển vì một lý do - thứ tự byte (endianness). Nhưng đây thường là lý do tốt nhất để sử dụng nó. Hãy hình dung ví dụ: bạn phải đọc số nhị phân 32 bit từ tệp và bạn biết đó là số cuối lớn. Mã của bạn phải chung chung và hoạt động chính xác trên các hệ thống endian lớn (ví dụ: một số ARM) và endian nhỏ (ví dụ x86). Vì vậy, bạn phải kiểm tra thứ tự byte. Nó nổi tiếng về thời gian biên dịch để bạn có thể viết constexprhàm: Bạn có thể viết một hàm để đạt được điều này:

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

Giải thích: biểu diễn nhị phân củaxbộ nhớ có thể là0000'0000'0000'0001(lớn) hoặc0000'0001'0000'0000(endian nhỏ). Sau khi diễn giải lại, việc tạo byte theopcon trỏ có thể tương ứng0000'0000hoặc0000'0001. Nếu bạn sử dụng phương pháp đúc tĩnh, nó sẽ luôn như vậy0000'0001, bất kể trường hợp nào đang được sử dụng.

BIÊN TẬP:

Trong phiên bản đầu tiên tôi đã thực hiện chức năng ví dụ is_little_endianđể được constexpr. Nó biên dịch tốt trên gcc mới nhất (8.3.0) nhưng tiêu chuẩn nói rằng nó là bất hợp pháp. Trình biên dịch clang từ chối biên dịch nó (đó là chính xác).


1
Ví dụ hay! Tôi sẽ thay thế viết tắt cho uint16_t và unsign char cho uint8_t để làm cho nó ít bị che khuất hơn cho con người.
Jan Turoň

@ JanTuroň đúng, chúng tôi không thể cho rằng shortmất 16 bit trong bộ nhớ. Đã sửa.
jaskmar

1
Ví dụ là sai. reinterpret_cast không được phép trong các hàm constexpr
Michael Veksler

1
Trước hết, mã này bị từ chối bởi cả clang mới nhất (7.0.0) và gcc (8.2.0). Thật không may, tôi đã không tìm thấy giới hạn trong ngôn ngữ chính thức. Tất cả những gì tôi có thể tìm thấy là social.msdn.microsoft.com/Forums/vstudio/en-US/ mẹo
Michael Veksler

2
Cụ thể hơn, en.cppreference.com/w/cpp/lingu/constant_expression (mục 16) nêu rõ rằng reinterpret_cast không thể được sử dụng trong một biểu thức không đổi. Ngoài ra, hãy xem github.com/cplusplus/draft/blob/master/ con / N3797.pdf (5.19 biểu thức không đổi) trang125-126 quy định rõ ràng về reinterpret_cast. Sau đó 7.1.5 Mục chỉ định constexpr 5 (trang 146) * Đối với hàm constexpr không mẫu, không mặc định ... nếu không có giá trị đối số nào tồn tại sao cho ... có thể là biểu thức con được đánh giá của biểu thức hằng số lõi (5.19 ), chương trình không được định hình *
Michael Veksler

20

Ý nghĩa của reinterpret_castkhông được xác định bởi tiêu chuẩn C ++. Do đó, trên lý thuyết reinterpret_castcó thể làm hỏng chương trình của bạn. Trong các trình biên dịch thực hành hãy cố gắng làm những gì bạn mong đợi, đó là diễn giải các bit của những gì bạn đang truyền vào như thể chúng là loại bạn đang truyền tới. Nếu bạn biết trình biên dịch bạn sẽ sử dụng, reinterpret_cast bạn có thể sử dụng trình biên dịch nào , nhưng phải nói rằng nó là trình di động sẽ nói dối.

Đối với trường hợp bạn mô tả, và hầu như bất kỳ trường hợp nào bạn có thể xem xét reinterpret_cast, bạn có thể sử dụng static_casthoặc một số thay thế khác thay thế. Trong số những điều khác, tiêu chuẩn có điều này để nói về những gì bạn có thể mong đợi static_cast(§5.2.9):

Một giá trị của loại con trỏ kiểu thành cv void có thể được chuyển đổi rõ ràng thành một con trỏ thành loại đối tượng. Một giá trị của con trỏ kiểu tới đối tượng được chuyển đổi thành con trỏ thành cv void void và quay lại kiểu con trỏ ban đầu sẽ có giá trị ban đầu.

Vì vậy, đối với trường hợp sử dụng của bạn, có vẻ như khá rõ ràng rằng ủy ban tiêu chuẩn hóa dành cho bạn sử dụng static_cast.


5
Không hoàn toàn sụp đổ chương trình của bạn. Tiêu chuẩn cung cấp một vài đảm bảo về reinterpret_cast. Chỉ là không nhiều như mọi người thường mong đợi.
jalf

1
Không nếu bạn sử dụng đúng cách. Đó là, reinterpret_cast từ A đến B đến A là hoàn toàn an toàn và được xác định rõ. Nhưng giá trị của B là không xác định, và vâng, nếu bạn dựa vào điều đó, điều tồi tệ có thể xảy ra. Nhưng bản thân diễn viên đã đủ an toàn, miễn là bạn chỉ sử dụng nó theo cách tiêu chuẩn cho phép. ;)
jalf

55
lol, tôi nghi ngờ rằng reinterpret_crash thực sự có thể làm hỏng chương trình của bạn. Nhưng reinterpret_cast sẽ không. ;)
jalf

5
<trớ trêu> Tôi đã thử nó trên trình biên dịch của mình và bằng cách nào đó, nó đã từ chối biên dịch reinterpret_crash. Không có cách nào một lỗi trình biên dịch sẽ ngăn tôi khỏi sự cố diễn giải lại chương trình của tôi. Tôi sẽ báo cáo lỗi càng sớm càng tốt! </ Trớ trêu>
paercebal

18
@paercebaltemplate<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }

12

Một cách sử dụng reinterpret_cast là nếu bạn muốn áp dụng các thao tác bitwise cho các float (IEEE 754). Một ví dụ về điều này là thủ thuật Root Inverse Square-Root nhanh:

https://en.wikipedia.org/wiki/Fast_inverse_sapes_root#Overview_of_the_code

Nó coi biểu diễn nhị phân của float là một số nguyên, dịch chuyển nó đúng và trừ nó khỏi một hằng số, do đó giảm một nửa và phủ định số mũ. Sau khi chuyển đổi trở lại phao, nó phải chịu phép lặp Newton-Raphson để làm cho phép tính gần đúng này chính xác hơn:

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;
}

Điều này ban đầu được viết bằng C, vì vậy sử dụng phôi C, nhưng diễn viên C ++ tương tự là reinterpret_cast.


1
error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast<double&>((reinterpret_cast<int64_t&>(d) >> 1) + (1L << 61))- ideone.com/6S4ijc
Orwellophile

1
Tiêu chuẩn nói rằng đây là hành vi không xác định: en.cppreference.com/w/cpp/lingu/reinterpret_cast (trong "loại bí danh")
Cris Luengo

@CrisLuengo Nếu tôi thay thế tất cả reinterpret_castbằng memcpy, nó vẫn là UB chứ?
Sandthorn

@sandthorn: Đây là UB theo tiêu chuẩn, nhưng nếu nó hoạt động cho kiến ​​trúc của bạn, đừng lo lắng về nó. Thủ thuật này là OK, tôi cho rằng, đối với bất kỳ trình biên dịch nào cho kiến ​​trúc Intel. Nó không thể hoạt động như dự định (hoặc thậm chí sụp đổ) trên các kiến ​​trúc khác - ví dụ có thể là phao và dài được lưu trữ trong các ngăn bộ nhớ riêng biệt (không phải tôi biết về bất kỳ kiến ​​trúc nào như vậy, đó chỉ là một đối số ...) . memcpychắc chắn sẽ làm cho nó hợp pháp.
Cris Luengo


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

Tôi đã cố gắng kết luận và viết một dàn diễn viên an toàn đơn giản bằng cách sử dụng các mẫu. Lưu ý rằng giải pháp này không đảm bảo để truyền con trỏ vào một chức năng.


1
Gì? Quan tâm làm gì? Đây chính xác là những gì reinterpret_castđã làm trong tình huống này: "Một con trỏ đối tượng có thể được chuyển đổi rõ ràng thành một con trỏ đối tượng thuộc một loại khác. [72] Khi một giá trị v của loại con trỏ đối tượng được chuyển đổi thành con trỏ đối tượng con trỏ thành cv T , kết quả là static_cast<cv T*>(static_cast<cv void*>(v)). " - N3797.
gạch dưới

Đối với c++2003tiêu chuẩn tôi KHÔNG thể tìm thấy điều reinterpret_castđóstatic_cast<cv T*>(static_cast<cv void*>(v))
Sasha Zezulinsky

1
OK, đúng, nhưng tôi không quan tâm đến một phiên bản từ 13 năm trước, và hầu hết các lập trình viên cũng không nên tránh (nếu có khả năng) họ có thể tránh nó. Câu trả lời và nhận xét phải thực sự phản ánh Tiêu chuẩn có sẵn mới nhất trừ khi có quy định khác ... IMHO. Dù sao, tôi đoán rằng Ủy ban cảm thấy cần phải bổ sung rõ ràng điều này sau năm 2003. (vì IIRC, nó giống như trong C ++ 11)
underscore_d

Trước khi C++03nó được C++98. Hàng tấn dự án đã sử dụng C ++ cũ thay vì xách tay C. Đôi khi bạn phải quan tâm đến tính di động. Ví dụ: bạn phải hỗ trợ cùng một mã trên Solaris, AIX, HPUX, Windows. Trường hợp nói đến sự phụ thuộc vào trình biên dịch và tính di động thì thật khó khăn. Vì vậy, một ví dụ điển hình về việc giới thiệu địa ngục khả chuyển là sử dụng reinterpret_castmã trong mã của bạn
Sasha Zezulinsky

một lần nữa, nếu như tôi, bạn rất vui khi chỉ giới hạn bản thân ở những nền tảng chơi tốt với phiên bản mới nhất và lớn nhất của ngôn ngữ, sự phản đối của bạn là một điểm cần thiết.
gạch dưới

1

Đầu tiên bạn có một số dữ liệu trong một loại cụ thể như int ở đây:

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

Sau đó, bạn muốn truy cập cùng một biến như một loại khác như float: Bạn có thể quyết định giữa

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

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

hoặc là

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

//this could be used in c and cpp

BRIEF: nó có nghĩa là cùng một bộ nhớ được sử dụng như một loại khác nhau. Vì vậy, bạn có thể chuyển đổi các biểu diễn nhị phân của phao dưới dạng int như trên thành phao. 0x80000000 là -0 chẳng hạn (mantissa và số mũ là null nhưng ký hiệu, msb, là một. Điều này cũng hoạt động cho đôi và đôi dài.

TỐI ƯU: Tôi nghĩ rằng reinterpret_cast sẽ được tối ưu hóa trong nhiều trình biên dịch, trong khi việc tạo c được thực hiện bằng con trỏ (giá trị phải được sao chép vào bộ nhớ, vì con trỏ không thể trỏ đến các thanh ghi cpu).

LƯU Ý: Trong cả hai trường hợp, bạn nên lưu giá trị được đúc trong một biến trước khi truyền! Macro này có thể giúp:

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

Đúng là "điều đó có nghĩa là cùng một bộ nhớ được sử dụng như một loại khác" nhưng nó bị giới hạn trong cặp loại cụ thể. Trong ví dụ của bạn reinterpret_casthình thức intđể float&là hành vi không xác định.
jaskmar

1

Một lý do để sử dụng reinterpret_castlà khi một lớp cơ sở không có vtable, nhưng một lớp dẫn xuất thì có. Trong trường hợp đó, static_castreinterpret_castsẽ dẫn đến các giá trị con trỏ khác nhau (đây sẽ là trường hợp không điển hình được đề cập bởi jalf ở trên ). Cũng giống như từ chối trách nhiệm, tôi không nói rằng đây là một phần của tiêu chuẩn, mà là việc thực hiện một số trình biên dịch rộng rãi.

Ví dụ, lấy mã dưới đây:

#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;
}

Mà xuất ra một cái gì đó như:

& b = 0x7ffe10e68b38
a = 0x7ffe10e68b40
ar = 0x7ffe10e68b38
chênh lệch = 2

Trong tất cả các trình biên dịch tôi đã thử (MSVC 2015 & 2017, clang 8.0.0, gcc 9.2, icc 19.0.1 - xem Godbolt cho 3 lần cuối ) kết quả của sự static_castkhác biệt so với reinterpret_cast2 (4 đối với MSVC). Trình biên dịch duy nhất để cảnh báo về sự khác biệt là tiếng kêu, với:

17:16: cảnh báo: 'reinterpret_cast' từ lớp 'B *' đến cơ sở của nó ở độ lệch khác không 'A *' hoạt động khác với 'static_cast' [-Wreinterpret-base-class]
const A * ar = reinterpret_cast (& b) ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
17:16: lưu ý: sử dụng 'static_cast' để điều chỉnh con trỏ chính xác trong khi upcasting
const A * ar = reinterpret_cast (& b) ;
^ ~~~~~~~~~~~~~~~
static_cast

Một cảnh báo cuối cùng là nếu lớp cơ sở không có thành viên dữ liệu (ví dụ int i;) thì clang, gcc và icc trả về cùng một địa chỉ reinterpret_castnhư trong static_cast, trong khi MSVC vẫn không có.


1

Đây là một biến thể của chương trình Avi Ginsburg, minh họa rõ ràng thuộc tính reinterpret_castđược đề cập bởi Chris Luengo, flodin và cmdLP: trình biên dịch xử lý vị trí bộ nhớ trỏ như thể nó là một đối tượng thuộc loại mới:

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

class A
{
public:
    int i;
};

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

int main()
{
    string s;
    B b;
    b.i = 0;
    A* as = static_cast<A*>(&b);
    A* ar = reinterpret_cast<A*>(&b);
    B* c = reinterpret_cast<B*>(ar);

    cout << "as->i = " << hex << setfill('0')  << as->i << "\n";
    cout << "ar->i = " << ar->i << "\n";
    cout << "b.i   = " << b.i << "\n";
    cout << "c->i  = " << c->i << "\n";
    cout << "\n";
    cout << "&(as->i) = " << &(as->i) << "\n";
    cout << "&(ar->i) = " << &(ar->i) << "\n";
    cout << "&(b.i) = " << &(b.i) << "\n";
    cout << "&(c->i) = " << &(c->i) << "\n";
    cout << "\n";
    cout << "&b = " << &b << "\n";
    cout << "as = " << as << "\n";
    cout << "ar = " << ar << "\n";
    cout << "c  = " << c  << "\n";

    cout << "Press ENTER to exit.\n";
    getline(cin,s);
}

Kết quả nào cho kết quả đầu ra như thế này:

as->i = 0
ar->i = 50ee64
b.i   = 0
c->i  = 0

&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i) = 00EFF978
&(c->i) = 00EFF978

&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c  = 00EFF974
Press ENTER to exit.

Có thể thấy rằng đối tượng B được xây dựng trong bộ nhớ dưới dạng dữ liệu dành riêng cho B, sau đó là đối tượng A được nhúng. Trả static_castvề chính xác địa chỉ của đối tượng A được nhúng và con trỏ được tạo bằng static_castcách cung cấp chính xác giá trị của trường dữ liệu. Con trỏ được tạo bởi các reinterpret_castđối xửb vị trí bộ nhớ như thể nó là một đối tượng A đơn giản và do đó, khi con trỏ cố lấy trường dữ liệu, nó sẽ trả về một số dữ liệu cụ thể B như thể đó là nội dung của trường này.

Một cách sử dụng reinterpret_castlà để chuyển đổi một con trỏ thành một số nguyên không dấu (khi các con trỏ và số nguyên không dấu có cùng kích thước):

int i; unsigned int u = reinterpret_cast<unsigned int>(&i);


-6

Trả lời nhanh: sử dụng static_castnếu nó biên dịch, nếu không thì dùng đến reinterpret_cast.


-16

Đọc FAQ ! Giữ dữ liệu C ++ trong C có thể có rủi ro.

Trong C ++, một con trỏ tới một đối tượng có thể được chuyển đổi thành void *không có phôi. Nhưng nó không đúng theo cách khác. Bạn sẽ cần một static_castđể lấy lại con trỏ ban đầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.