Saya sedikit bingung dengan penerapan reinterpret_cast
vs static_cast
. Dari apa yang saya baca aturan umum adalah dengan menggunakan pemeran statis ketika jenis dapat ditafsirkan pada waktu kompilasi maka kata itu static
. Ini adalah pemeran yang digunakan kompilator C ++ secara internal untuk pemeran implisit juga.
reinterpret_cast
s berlaku dalam dua skenario:
- mengkonversi tipe integer ke tipe pointer dan sebaliknya
- konversi satu tipe pointer ke yang lain. Ide umum yang saya dapatkan adalah ini tidak dapat diangkut dan harus dihindari.
Di mana saya sedikit bingung adalah salah satu penggunaan yang saya butuhkan, saya memanggil C ++ dari C dan kode C perlu berpegang pada objek C ++ jadi pada dasarnya ia memegang a void*
. Para pemeran apa yang harus digunakan untuk mengkonversi antara tipe void *
dan Class?
Saya telah melihat penggunaan keduanya static_cast
dan reinterpret_cast
? Meskipun dari apa yang saya baca tampaknya static
lebih baik karena para pemain dapat terjadi pada waktu kompilasi? Meskipun dikatakan menggunakan reinterpret_cast
untuk mengkonversi dari satu jenis pointer ke yang lain?
reinterpret_cast
tidak terjadi pada saat run time. Keduanya adalah pernyataan waktu kompilasi. Dari en.cppreference.com/w/cpp/language/reinterpret_cast : "Tidak seperti static_cast, tetapi seperti const_cast, ekspresi reinterpret_cast tidak dapat dikompilasi dengan instruksi CPU apa pun. Ini hanyalah arahan kompiler yang memerintahkan kompiler untuk memerintahkan urutan bit (representasi objek) ekspresi seolah-olah memiliki tipe new_type. "Jawaban:
Standar C ++ menjamin yang berikut:
static_cast
ing pointer ke dan darivoid*
mempertahankan alamat. Yaitu di berikut inia
,b
danc
semuanya menunjuk ke alamat yang sama:reinterpret_cast
hanya menjamin bahwa jika Anda melemparkan pointer ke tipe yang berbeda, dan kemudianreinterpret_cast
kembali ke tipe yang asli , Anda mendapatkan nilai aslinya. Jadi sebagai berikut:a
danc
mengandung nilai yang sama, tetapi nilaib
tidak ditentukan. (dalam praktiknya biasanya akan berisi alamat yang sama dengana
danc
, tetapi itu tidak ditentukan dalam standar, dan itu mungkin tidak benar pada mesin dengan sistem memori yang lebih kompleks.)Untuk casting ke dan dari
void*
,static_cast
harus lebih disukai.sumber
b
tidak ditentukan lagi dalam C ++ 11 saat menggunakanreinterpret_cast
. Dan di C ++ 03 pemainint*
untukvoid*
dilarang untuk dilakukan denganreinterpret_cast
(walaupun kompiler tidak menerapkan itu dan itu tidak praktis, maka diubah untuk C ++ 11).Satu kasus ketika
reinterpret_cast
diperlukan adalah ketika berinteraksi dengan tipe data buram. Ini sering terjadi pada API vendor yang tidak dapat dikontrol oleh pemrogram. Berikut adalah contoh yang dibuat-buat di mana vendor menyediakan API untuk menyimpan dan mengambil data global sewenang-wenang:Untuk menggunakan API ini, programmer harus memasukkan data mereka ke
VendorGlobalUserData
dan kembali lagi.static_cast
tidak akan berfungsi, seseorang harus menggunakanreinterpret_cast
:Di bawah ini adalah implementasi yang dibuat dari sampel API:
sumber
void*
untuk itu?USpoofChecker*
, di manaUSpoofChecker
struct kosong. Namun, di bawah kap, setiap kali Anda melewati aUSpoofChecker*
, itu mengalamireinterpret_cast
tipe C ++ internal.Jawaban singkatnya: Jika Anda tidak tahu apa
reinterpret_cast
kepanjangan dari , jangan gunakan itu. Jika Anda akan membutuhkannya di masa depan, Anda akan tahu.Jawaban lengkap:
Mari kita pertimbangkan tipe angka dasar.
Ketika Anda mengonversi misalnya
int(12)
keunsigned float (12.0f)
prosesor Anda perlu menjalankan beberapa perhitungan karena kedua angka memiliki representasi bit yang berbeda. Ini adalah apastatic_cast
singkatan.Di sisi lain, ketika Anda memanggil
reinterpret_cast
CPU tidak meminta perhitungan apa pun. Itu hanya memperlakukan satu set bit dalam memori seperti jika itu memiliki tipe lain. Jadi, ketika Anda masukint*
kefloat*
dengan kata kunci ini, nilai baru (setelah pointer dereferecing) tidak ada hubungannya dengan nilai lama dalam arti matematika.Contoh: Memang benar bahwa
reinterpret_cast
tidak portabel karena satu alasan - byte order (endianness). Tapi ini sering kali merupakan alasan terbaik untuk menggunakannya. Mari kita bayangkan contohnya: Anda harus membaca nomor biner 32bit dari file, dan Anda tahu itu adalah big endian. Kode Anda harus generik dan berfungsi dengan baik pada sistem big endian (mis. ARM) dan little endian (mis. X86). Jadi, Anda harus memeriksa urutan byte.Ini terkenal pada waktu kompilasi sehingga Anda dapat menulisAnda dapat menulis fungsi untuk mencapai ini:constexpr
fungsi:Penjelasan: representasi biner
x
dalam memori bisa0000'0000'0000'0001
(besar) atau0000'0001'0000'0000
(little endian). Setelah menafsirkan ulang-casting byte di bawahp
pointer bisa masing-masing0000'0000
atau0000'0001
. Jika Anda menggunakan pengecoran statis, itu akan selalu terjadi0000'0001
, tidak peduli apa pun endianness yang digunakan.EDIT:
Pada versi pertama saya membuat contoh fungsi
is_little_endian
menjadiconstexpr
. Ini mengkompilasi dengan baik pada gcc terbaru (8.3.0) tetapi standar mengatakan itu ilegal. Compiler dentang menolak untuk mengkompilasinya (yang benar).sumber
short
dibutuhkan 16 bit dalam memori. Dikoreksi.Arti
reinterpret_cast
tidak didefinisikan oleh standar C ++. Oleh karena itu, secara teorireinterpret_cast
dapat crash program Anda. Dalam praktiknya kompiler mencoba melakukan apa yang Anda harapkan, yaitu menafsirkan bit dari apa yang Anda lewati seolah-olah mereka adalah tipe yang Anda gunakan. Jika Anda tahu apa yang akan Anda gunakan dengan kompiler Andareinterpret_cast
dapat menggunakannya, tetapi mengatakan bahwa itu portabel akan berbohong.Untuk kasus yang Anda gambarkan, dan hampir semua kasus di mana Anda dapat mempertimbangkan
reinterpret_cast
, Anda dapat menggunakanstatic_cast
atau beberapa alternatif lain sebagai gantinya. Di antara hal-hal lain standar ini mengatakan tentang apa yang dapat Anda harapkan daristatic_cast
(§5.2.9):Jadi untuk kasus penggunaan Anda, tampaknya cukup jelas bahwa komite standardisasi dimaksudkan untuk Anda gunakan
static_cast
.sumber
reinterpret_crash
. Tidak mungkin bug kompiler akan menghentikan saya dari crash program reinterpreting saya. Saya akan melaporkan bug ASAP! </irony>template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
Salah satu penggunaan reinterpret_cast adalah jika Anda ingin menerapkan operasi bitwise ke (IEEE 754) mengapung. Salah satu contohnya adalah trik Fast Inverse Square-Root:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
Ini memperlakukan representasi biner dari float sebagai bilangan bulat, menggesernya ke kanan dan mengurangi dari konstanta, sehingga mengurangi separuh dan meniadakan eksponen. Setelah mengonversi kembali ke float, ia mengalami iterasi Newton-Raphson untuk membuat perkiraan ini lebih tepat:
Ini awalnya ditulis dalam C, jadi menggunakan C casts, tetapi C ++ cast analog adalah reinterpret_cast.
sumber
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/6S4ijcreinterpret_cast
denganmemcpy
, apakah masih UB?memcpy
pasti akan membuatnya legal.Anda bisa menggunakan reinterprete_cast untuk memeriksa warisan pada waktu kompilasi.
Lihat di sini: Menggunakan reinterpret_cast untuk memeriksa warisan pada waktu kompilasi
sumber
Saya mencoba untuk menyimpulkan dan menulis gips sederhana yang aman menggunakan templat. Perhatikan bahwa solusi ini tidak menjamin untuk mengarahkan pointer pada suatu fungsi.
sumber
reinterpret_cast
sudah tidak dalam situasi ini: ". Sebuah pointer objek dapat secara eksplisit dikonversi ke pointer objek dari tipe yang berbeda [72] Ketika prvaluev
objek jenis pointer dikonversi ke objek jenis pointer‘pointer ke cvT
’, hasilnya adalahstatic_cast<cv T*>(static_cast<cv void*>(v))
. " - N3797.c++2003
standar saya TIDAK dapat menemukan yangreinterpret_cast
tidakstatic_cast<cv T*>(static_cast<cv void*>(v))
C++03
ituC++98
. Banyak proyek menggunakan C ++ lama daripada portable C. Kadang-kadang Anda harus peduli tentang portabilitas. Misalnya Anda harus mendukung kode yang sama pada Solaris, AIX, HPUX, Windows. Dalam hal ketergantungan compiler dan portabilitas, ini rumit. Jadi contoh yang baik untuk memperkenalkan neraka portabilitas adalah dengan menggunakanreinterpret_cast
kode AndaPertama, Anda memiliki beberapa data dalam tipe tertentu seperti int di sini:
Kemudian Anda ingin mengakses variabel yang sama dengan tipe lain seperti float: Anda dapat memutuskan di antaranya
atau
SINGKAT: itu berarti memori yang sama digunakan sebagai tipe yang berbeda. Jadi Anda bisa mengonversi representasi biner dari float sebagai tipe int seperti di atas menjadi float. 0x80000000 adalah -0 misalnya (mantissa dan eksponen adalah nol tetapi tandanya, msb, adalah satu. Ini juga berfungsi untuk ganda dan panjang ganda.
MENGOPTIMALKAN: Saya pikir reinterpret_cast akan dioptimalkan dalam banyak kompiler, sementara c-casting dibuat oleh pointerarithmetic (nilai harus disalin ke memori, menyebabkan pointer tidak dapat menunjuk ke register cpu-register).
CATATAN: Dalam kedua kasus, Anda harus menyimpan nilai yang dicor dalam sebuah variabel sebelum dilemparkan! Makro ini dapat membantu:
sumber
reinterpret_cast
formulir contoh Andaint
untukfloat&
perilaku tidak terdefinisi.Salah satu alasan untuk digunakan
reinterpret_cast
adalah ketika kelas dasar tidak memiliki vtable, tetapi kelas turunan tidak. Dalam hal ini,static_cast
danreinterpret_cast
akan menghasilkan nilai pointer yang berbeda (ini akan menjadi kasus atipikal yang disebutkan oleh jalf di atas ). Sama seperti penafian, saya tidak menyatakan bahwa ini adalah bagian dari standar, tetapi penerapan beberapa kompiler yang tersebar luas.Sebagai contoh, ambil kode di bawah ini:
Yang menghasilkan sesuatu seperti:
Dalam semua kompiler yang saya coba (MSVC 2015 & 2017, clang 8.0.0, gcc 9.2, icc 19.0.1 - lihat godbolt untuk 3 terakhir ) hasil
static_cast
berbeda dari yangreinterpret_cast
oleh 2 (4 untuk MSVC). Satu-satunya kompiler yang memperingatkan tentang perbedaannya adalah dentang, dengan:Satu peringatan terakhir adalah bahwa jika kelas dasar tidak memiliki anggota data (misalnya
int i;
) maka dentang, gcc, dan icc mengembalikan alamat yang samareinterpret_cast
dengan untukstatic_cast
, sedangkan MSVC masih tidak.sumber
Berikut adalah varian dari program Avi Ginsburg yang dengan jelas menggambarkan properti yang
reinterpret_cast
disebutkan oleh Chris Luengo, flodin, dan cmdLP: bahwa kompiler memperlakukan lokasi memori menunjuk-ke seolah-olah itu adalah objek dari tipe baru:Yang menghasilkan output seperti ini:
Dapat dilihat bahwa objek B dibangun dalam memori sebagai data spesifik-B pertama, diikuti oleh objek A tertanam. Yang
static_cast
benar mengembalikan alamat objek tertanam A, dan pointer yang dibuat olehstatic_cast
dengan benar memberikan nilai dari bidang data. Pointer yang dihasilkan olehreinterpret_cast
suguhanb
lokasi memori seolah-olah itu adalah objek A polos, dan jadi ketika pointer mencoba untuk mendapatkan bidang data itu mengembalikan beberapa data spesifik-B seolah-olah itu adalah isi bidang ini.Salah satu penggunaan
reinterpret_cast
adalah untuk mengkonversi pointer ke integer unsigned (ketika pointer dan integer unsigned berukuran sama):int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
sumber
Jawaban cepat: gunakan
static_cast
jika dikompilasi, jika tidak gunakanreinterpret_cast
.sumber
Baca FAQ ! Memegang data C ++ di C bisa berisiko.
Dalam C ++, pointer ke objek dapat dikonversi menjadi
void *
tanpa gips. Tapi itu tidak benar sebaliknya. Anda perlustatic_cast
mendapatkan kembali pointer asli.sumber