Untuk beberapa contoh konkret yang bermanfaat menggunakan berbagai jenis gips, Anda dapat memeriksa jawaban pertama pada pertanyaan serupa di topik lain ini .
TeaMonkie
2
Anda dapat menemukan jawaban yang sangat bagus untuk pertanyaan Anda di atas. Tapi saya ingin menempatkan satu poin lagi di sini, @ e.James "Tidak ada yang bisa dilakukan oleh operator c ++ baru ini dan c style cast tidak bisa. Ini ditambahkan kurang lebih untuk keterbacaan kode yang lebih baik."
BreakBadSP
@BreakBadSP Cast baru tidak hanya untuk pembacaan kode yang lebih baik. Mereka ada di sana untuk membuatnya lebih sulit untuk melakukan hal-hal berbahaya, seperti membuang const atau mengarahkan pointer daripada nilai-nilai mereka. static_cast memiliki jauh lebih sedikit kemungkinan untuk melakukan sesuatu yang berbahaya daripada pemeran gaya ac!
FourtyTwo
@FourtyTwo setuju
BreakBadSP
Jawaban:
2571
static_castadalah pemeran pertama yang harus Anda coba gunakan. Itu melakukan hal-hal seperti konversi implisit antara jenis (seperti intuntuk float, atau penunjuk ke void*), dan juga dapat memanggil fungsi konversi eksplisit (atau yang implisit). Dalam banyak kasus, menyatakan secara eksplisit static_casttidak perlu, tetapi penting untuk dicatat bahwa T(something)sintaksinya setara dengan (T)somethingdan harus dihindari (lebih lanjut tentang itu nanti). T(something, something_else)Namun, A aman dan dijamin untuk memanggil konstruktor.
static_castjuga dapat dilemparkan melalui hierarki warisan. Tidak perlu ketika casting ke atas (menuju kelas dasar), tetapi ketika casting ke bawah itu dapat digunakan selama tidak dibuang melalui virtualwarisan. Itu tidak melakukan pengecekan, bagaimanapun, dan itu adalah perilaku tidak terdefinisi untuk static_castmenurunkan hierarki ke tipe yang sebenarnya bukan tipe objek.
const_castdapat digunakan untuk menghapus atau menambah constvariabel; tidak ada pemain C ++ lainnya yang mampu menghapusnya (bahkan tidak reinterpret_cast). Penting untuk dicatat bahwa memodifikasi constnilai sebelumnya hanya tidak terdefinisi jika variabel aslinya const; jika Anda menggunakannya untuk constmelepas referensi ke sesuatu yang tidak dideklarasikan dengannya const, itu aman. Ini bisa bermanfaat ketika kelebihan fungsi anggota berdasarkan const, misalnya. Itu juga dapat digunakan untuk menambah constobjek, seperti untuk memanggil fungsi anggota yang berlebihan.
const_castjuga bekerja dengan cara yang sama volatile, meskipun itu kurang umum.
dynamic_castsecara eksklusif digunakan untuk menangani polimorfisme. Anda bisa melemparkan pointer atau referensi ke tipe polimorfik apa pun ke tipe kelas lain (tipe polimorfik memiliki setidaknya satu fungsi virtual, dideklarasikan atau diwarisi). Anda dapat menggunakannya untuk lebih dari sekedar melakukan casting ke bawah - Anda dapat melakukan gerakan sideways atau bahkan ke atas. The dynamic_castakan mencari objek yang diinginkan dan mengembalikannya jika mungkin. Jika tidak bisa, itu akan kembali nullptrdalam kasus penunjuk, atau melempar std::bad_castdalam kasus referensi.
dynamic_castmemiliki beberapa keterbatasan. Ini tidak berfungsi jika ada beberapa objek dari jenis yang sama dalam hierarki warisan (yang disebut 'berlian yang ditakuti') dan Anda tidak menggunakan virtualwarisan. Itu juga hanya bisa melalui warisan publik - itu akan selalu gagal untuk melakukan perjalanan melalui protectedatau privatewarisan. Namun, ini jarang menjadi masalah, karena bentuk-bentuk warisan seperti itu jarang terjadi.
reinterpret_castadalah pemain yang paling berbahaya, dan harus digunakan sangat hemat. Itu mengubah satu jenis langsung ke yang lain - seperti melemparkan nilai dari satu pointer ke yang lain, atau menyimpan pointer di dalam int, atau segala macam hal buruk lainnya. Sebagian besar, satu-satunya jaminan yang Anda dapatkan reinterpret_castadalah bahwa jika Anda mengembalikan hasilnya ke jenis aslinya, Anda akan mendapatkan nilai yang sama persis (tetapi tidak jika jenis perantara lebih kecil dari jenis aslinya). Ada sejumlah konversi yang reinterpret_casttidak dapat dilakukan juga. Ini digunakan terutama untuk konversi aneh dan manipulasi bit, seperti mengubah aliran data mentah menjadi data aktual, atau menyimpan data dalam bit rendah dari pointer untuk menyelaraskan data.
Pemain gaya-C dan pemain gaya- fungsi adalah masing-masing pemain yang menggunakan (type)objectatau type(object), dan secara fungsional setara. Mereka didefinisikan sebagai yang pertama dari berikut yang berhasil:
const_cast
static_cast (meskipun mengabaikan batasan akses)
static_cast (lihat di atas), lalu const_cast
reinterpret_cast
reinterpret_cast, kemudian const_cast
Karena itu dapat digunakan sebagai pengganti gips lain dalam beberapa kasus, tetapi bisa sangat berbahaya karena kemampuan untuk berubah menjadi reinterpret_cast, dan yang terakhir harus dipilih ketika pengecoran eksplisit diperlukan, kecuali Anda yakin static_castakan berhasil atau reinterpret_castakan gagal . Meski begitu, pertimbangkan opsi yang lebih panjang dan lebih eksplisit.
static_castPemain gaya-C juga mengabaikan kontrol akses saat melakukan , yang berarti mereka memiliki kemampuan untuk melakukan operasi yang tidak bisa dilakukan oleh pemain lain. Ini sebagian besar adalah kludge, dan dalam pikiran saya hanyalah alasan lain untuk menghindari gips C-style.
dynamic_cast hanya untuk tipe polimorfik. Anda hanya perlu menggunakannya saat Anda melakukan casting ke kelas turunan. static_cast tentu saja merupakan opsi pertama kecuali Anda secara khusus membutuhkan fungsi dynamic_cast. Ini bukan "peluru pemeriksa" jenis peluru perak ajaib secara umum.
jalf
2
Jawaban bagus! Satu komentar cepat: static_cast mungkin diperlukan untuk membuat hierarki jika Anda memiliki Derived * & untuk dilemparkan ke dalam Base * &, karena double pointer / referensi tidak secara otomatis memasang hierarki. Saya menemukan situasi (terus terang, tidak umum) seperti itu dua menit yang lalu. ;-)
bartgol
5
* "tidak ada pemeran C ++ lainnya yang mampu menghapus const(bahkan tidak reinterpret_cast)" ... benarkah? Bagaimana dengan reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
user541686
29
Saya pikir detail penting yang hilang di atas adalah dynamic_cast memiliki penalti kinerja run-time dibandingkan dengan statis atau reinterpret_cast. Ini penting, misalnya dalam perangkat lunak waktu nyata.
jfritz42
5
Mungkin perlu disebutkan bahwa reinterpret_castseringkali merupakan senjata pilihan ketika berhadapan dengan set data API tipe API yang tidak jelas
Class Skeleton
333
Gunakan dynamic_castuntuk mengkonversi pointer / referensi dalam hierarki warisan.
Gunakan static_castuntuk konversi jenis biasa.
Gunakan reinterpret_castuntuk menafsirkan ulang pola bit tingkat rendah. Gunakan dengan sangat hati-hati.
Gunakan const_castuntuk membuang const/volatile. Hindari ini kecuali Anda terjebak menggunakan API const-salah.
Hati-hati dengan dynamic_cast. Itu bergantung pada RTTI dan ini tidak akan berfungsi seperti yang diharapkan melintasi batas-batas perpustakaan bersama. Hanya karena Anda membangun perpustakaan yang dapat dieksekusi dan dibagikan secara independen di sana, tidak ada cara standar untuk menyinkronkan RTTI di berbagai bangunan. Untuk alasan ini di perpustakaan Qt terdapat qobject_cast <> yang menggunakan info jenis QObject untuk memeriksa jenis.
user3150128
198
(Banyak penjelasan teoretis dan konseptual telah diberikan di atas)
Di bawah ini adalah beberapa contoh praktis ketika saya menggunakan static_cast , dynamic_cast , const_cast , reinterpret_cast .
OnEventData(void* pData){......// pData is a void* pData, // EventData is a structure e.g. // typedef struct _EventData {// std::string id;// std:: string remote_id;// } EventData;// On Some Situation a void pointer *pData// has been static_casted as // EventData* pointer EventData*evtdata =static_cast<EventData*>(pData);.....}
Teori beberapa jawaban lain baik, tetapi masih membingungkan, melihat contoh-contoh ini setelah membaca jawaban lain benar-benar masuk akal. Itu tanpa contoh, saya masih tidak yakin, tetapi dengan mereka, saya sekarang yakin tentang apa artinya jawaban yang lain.
Solx
1
Tentang penggunaan terakhir reinterpret_cast: bukankah ini sama dengan menggunakan static_cast<char*>(&val)?
Lorenzo Belli
3
@ LorenzoBelli Tentu saja tidak. Apakah kamu sudah mencobanya? Yang terakhir ini tidak valid C ++ dan memblokir kompilasi. static_casthanya berfungsi di antara jenis dengan konversi yang ditentukan, hubungan yang terlihat menurut pewarisan, atau ke / dari void *. Untuk yang lainnya, ada gips lainnya. reinterpret castuntuk char *jenis apa pun diizinkan untuk memungkinkan membaca representasi objek apa pun - dan satu-satunya kasus di mana kata kunci tersebut berguna, bukan generator yang merajalela dari implementasi- / perilaku tidak terdefinisi. Tetapi ini tidak dianggap sebagai konversi 'normal', jadi tidak diperbolehkan oleh (biasanya) sangat konservatif static_cast.
underscore_d
2
reinterpret_cast cukup umum ketika Anda bekerja dengan perangkat lunak sistem seperti database. Sebagian besar kasus Anda menulis manajer halaman Anda sendiri yang tidak tahu tentang apa tipe data yang disimpan di halaman dan hanya mengembalikan pointer kosong. Terserah ke tingkat yang lebih tinggi untuk melakukan pemeran ulang dan menyimpulkannya sebagai apa pun yang mereka inginkan.
Sohaib
1
Contoh const_cast menunjukkan Perilaku Tidak Terdefinisi. Variabel yang dideklarasikan sebagai const tidak dapat dide-const-ed. Namun, variabel yang dideklarasikan sebagai non-const yang diteruskan ke fungsi yang mengambil referensi const dapat dalam fungsi tersebut dide-const-ed tanpa itu menjadi UB.
Johann Gerell
99
Mungkin membantu jika Anda tahu sedikit tentang internal ...
static_cast
Kompiler C ++ sudah tahu cara mengkonversi antara jenis scaler seperti float ke int. Gunakan static_castuntuk mereka.
Ketika Anda meminta kompiler untuk mengkonversi dari tipe Ake B, konstruktor static_castpanggilan Blewat Asebagai param. Atau, Adapat memiliki operator konversi (yaitu A::operator B()). Jika Btidak memiliki konstruktor seperti itu, atau Atidak memiliki operator konversi, maka Anda mendapatkan kesalahan waktu kompilasi.
Keluarkan dari A*untuk B*selalu berhasil jika A dan B berada dalam hierarki warisan (atau batal) jika tidak Anda mendapatkan kesalahan kompilasi.
Gotcha : Jika Anda melempar penunjuk dasar ke penunjuk yang diturunkan tetapi jika objek yang sebenarnya tidak benar-benar berasal jenis maka Anda tidak mendapatkan kesalahan. Anda mendapatkan pointer buruk dan sangat mungkin segfault saat runtime. Sama berlaku untuk A&untuk B&.
Gotcha : Cast dari Derived to Base atau viceversa membuat salinan baru ! Bagi orang-orang yang datang dari C # / Java, ini bisa menjadi kejutan besar karena hasilnya pada dasarnya adalah objek yang dibuat dari Derived.
dynamic_cast
dynamic_cast menggunakan informasi tipe runtime untuk mencari tahu apakah cast valid. Misalnya, (Base*)untuk (Derived*)dapat gagal jika pointer tidak benar-benar tipe berasal.
Ini berarti, dynamic_cast sangat mahal dibandingkan dengan static_cast!
Untuk A*ke B*, jika casting tidak valid maka dynamic_cast akan mengembalikan nullptr.
Untuk A&ke B&jika pemain tidak valid maka dynamic_cast akan membuang pengecualian bad_cast.
Tidak seperti gips lainnya, ada overhead runtime.
const_cast
Sementara static_cast dapat melakukan non-const ke const, tetapi tidak bisa sebaliknya. Const_cast dapat melakukan keduanya.
Salah satu contoh di mana ini berguna adalah iterasi melalui beberapa wadah seperti set<T>yang hanya mengembalikan elemen-elemennya sebagai const untuk memastikan Anda tidak mengubah kuncinya. Namun jika maksud Anda adalah untuk memodifikasi anggota non-kunci objek maka itu harus ok. Anda dapat menggunakan const_cast untuk menghapus constness.
Contoh lain adalah ketika Anda ingin menerapkan T& SomeClass::foo()juga const T& SomeClass::foo() const. Untuk menghindari duplikasi kode, Anda dapat menerapkan const_cast untuk mengembalikan nilai satu fungsi dari yang lain.
reinterpret_cast
Ini pada dasarnya mengatakan bahwa mengambil byte ini di lokasi memori ini dan menganggapnya sebagai objek yang diberikan.
Misalnya, Anda dapat memuat 4 byte float ke 4 byte int untuk melihat bagaimana bit di float terlihat.
Jelas, jika data tidak benar untuk jenisnya, Anda mungkin mendapatkan segfault.
Saya menambahkan informasi operator konversi, tetapi ada beberapa hal lain yang harus diperbaiki juga dan saya merasa tidak nyaman memperbarui ini terlalu banyak. Item adalah: 1. If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.Anda mendapatkan UB yang dapat menghasilkan segfault saat runtime jika Anda beruntung. 2. Cor dinamis dapat juga digunakan dalam cross casting. 3. Pemain cast dapat menghasilkan UB dalam beberapa kasus. Menggunakan mutablemungkin merupakan pilihan yang lebih baik untuk menerapkan keteguhan logis.
Adrian
1
@Adrian Anda benar dalam semua hitungan. Jawabannya ditulis untuk orang-orang di level pemula atau kurang lebih dan saya tidak ingin membanjiri mereka dengan semua komplikasi lain yang datang bersama mutable, cross casting dll.
Saya tidak pernah menggunakan reinterpret_cast, dan bertanya-tanya apakah berlari ke case yang membutuhkannya bukan bau desain yang buruk. Pada basis kode yang saya kerjakan banyak dynamic_castdigunakan. Bedanya dengan static_castadalah bahwa dynamic_casttidak pengecekan runtime yang mungkin (lebih aman) atau mungkin tidak (overhead) menjadi apa yang Anda inginkan (lihat MSDN ).
Saya telah menggunakan reintrepret_cast untuk satu tujuan - mendapatkan bit dari double (ukuran yang sama panjangnya di platform saya).
Joshua
2
reinterpret_cast diperlukan misalnya untuk bekerja dengan objek COM. CoCreateInstance () memiliki parameter keluaran tipe void ** (parameter terakhir), di mana Anda akan melewatkan pointer yang dinyatakan sebagai misalnya "INetFwPolicy2 * pNetFwPolicy2". Untuk melakukan itu, Anda perlu menulis sesuatu seperti reinterpret_cast <void **> (& pNetFwPolicy2).
Serge Rogatch
1
Mungkin ada pendekatan yang berbeda, tetapi saya gunakan reinterpret_castuntuk mengekstrak potongan data dari array. Sebagai contoh jika saya memiliki char*buffer besar yang penuh dengan data biner yang saya butuhkan untuk bergerak dan mendapatkan primitif individu dari berbagai jenis. Sesuatu seperti ini:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
James Matta
Saya tidak pernah menggunakan reinterpret_cast, tidak ada banyak kegunaan untuk itu.
Pika sang Penyihir Paus
Secara pribadi saya hanya pernah melihat reinterpret_castdigunakan untuk satu alasan. Saya telah melihat data objek mentah disimpan ke tipe data "gumpalan" dalam database, kemudian ketika data tersebut diambil dari database, reinterpret_castdigunakan untuk mengubah data mentah ini menjadi objek.
ImaginaryHuman072889
15
Selain jawaban lain sejauh ini, berikut adalah contoh yang tidak jelas di mana static_casttidak cukup sehingga reinterpret_castdiperlukan. Misalkan ada fungsi yang dalam parameter output mengembalikan pointer ke objek dari kelas yang berbeda (yang tidak berbagi kelas basis umum). Contoh nyata dari fungsi tersebut adalah CoCreateInstance()(lihat parameter terakhir, yang sebenarnya void**). Misalkan Anda meminta kelas objek tertentu dari fungsi ini, jadi Anda tahu sebelumnya jenis untuk pointer (yang sering Anda lakukan untuk objek COM). Dalam hal ini Anda tidak dapat memasukkan pointer ke pointer Anda void**dengan static_cast: you need reinterpret_cast<void**>(&yourPointer).
Dalam kode:
#include<windows.h>#include<netfw.h>.....INetFwPolicy2* pNetFwPolicy2 =nullptr;
HRESULT hr =CoCreateInstance(__uuidof(NetFwPolicy2),nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),//static_cast<void**>(&pNetFwPolicy2) would give a compile errorreinterpret_cast<void**>(&pNetFwPolicy2));
Namun, static_castberfungsi untuk pointer sederhana (bukan pointer ke pointer), sehingga kode di atas dapat ditulis ulang untuk menghindari reinterpret_cast(dengan harga variabel tambahan) dengan cara berikut:
Bukankah itu akan bekerja seperti &static_cast<void*>(pNetFwPolicy2)bukan static_cast<void**>(&pNetFwPolicy2)?
jp48
9
Sementara jawaban lain menggambarkan dengan baik semua perbedaan antara cast C ++, saya ingin menambahkan catatan singkat mengapa Anda tidak harus menggunakan cast C-style (Type) vardan Type(var).
Untuk pemain C ++ pemula C-style terlihat seperti menjadi operasi superset lebih dari C ++ gips (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) dan seseorang dapat lebih memilih mereka daripada pemain C ++ . Sebenarnya pemeran C-style adalah superset dan lebih pendek untuk menulis.
Masalah utama para pemain C-style adalah mereka menyembunyikan niat sebenarnya dari para pemain. Gips C-style dapat melakukan hampir semua jenis gips dari gips yang biasanya aman dilakukan oleh static_cast <> () dan dynamic_cast <> () ke gips yang berpotensi berbahaya seperti const_cast <> (), di mana pengubah konst dapat dihapus sehingga variabel konst dapat dimodifikasi dan reinterpret_cast <> () yang bahkan dapat menafsirkan kembali nilai integer ke pointer.
Ini contohnya.
int a=rand();// Random number.int* pa1=reinterpret_cast<int*>(a);// OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.int* pa2=static_cast<int*>(a);// Compiler error.int* pa3=dynamic_cast<int*>(a);// Compiler error.int* pa4=(int*) a;// OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.*pa4=5;// Program crashes.
Alasan utama mengapa para pemain C ++ ditambahkan ke dalam bahasa ini adalah untuk memungkinkan pengembang untuk mengklarifikasi niatnya - mengapa ia akan melakukan pemeran itu. Dengan menggunakan gips C-style yang benar-benar valid di C ++ Anda membuat kode Anda kurang mudah dibaca dan lebih rentan kesalahan terutama untuk pengembang lain yang tidak membuat kode Anda. Jadi untuk membuat kode Anda lebih mudah dibaca dan eksplisit, Anda harus selalu lebih suka cast C ++ daripada cast gaya-C.
Berikut adalah kutipan pendek dari buku Bjarne Stroustrup (penulis C ++) The C ++ Programming Language edisi ke 4 - halaman 302.
Pemain gaya C ini jauh lebih berbahaya daripada operator konversi yang disebutkan karena notasi lebih sulit dikenali dalam program besar dan jenis konversi yang dimaksudkan oleh programmer tidak eksplisit.
Hanya baris (4) yang dikompilasi tanpa kesalahan. Hanya reinterpret_cast yang dapat digunakan untuk mengonversi pointer ke objek ke pointer ke jenis objek apa pun yang tidak terkait.
Satu hal yang perlu diperhatikan adalah: dynamic_cast akan gagal pada saat run-time, tetapi pada kebanyakan kompiler, ia juga akan gagal untuk dikompilasi karena tidak ada fungsi virtual dalam struct dari pointer yang sedang di-cast, artinya dynamic_cast hanya akan bekerja dengan pointer kelas polymorphic saja .
Kapan menggunakan C ++ cast :
Gunakan static_cast sebagai setara dengan pemeran gaya-C yang melakukan konversi nilai, atau ketika kita perlu secara eksplisit memasang sebuah penunjuk dari sebuah kelas ke kelasnya.
Gunakan const_cast untuk menghapus kualifikasi const.
Gunakan reinterpret_cast untuk melakukan konversi yang tidak aman dari tipe pointer ke dan dari integer dan tipe pointer lainnya. Gunakan ini hanya jika kita tahu apa yang sedang kita lakukan dan kita memahami masalah aliasing.
static_castvs dynamic_castvs reinterpret_castinternal melihat pada downcast / upcast
Dalam jawaban ini, saya ingin membandingkan tiga mekanisme ini pada contoh konkret / downcast konkret dan menganalisis apa yang terjadi pada pointer / memori / perakitan yang mendasari untuk memberikan pemahaman konkret tentang bagaimana mereka membandingkan.
Saya percaya bahwa ini akan memberikan intuisi yang baik tentang bagaimana para pemain berbeda:
static_cast: apakah satu alamat offset pada saat runtime (dampak runtime rendah) dan tidak ada keamanan yang memeriksa bahwa downcast benar.
dyanamic_cast: apakah alamat yang sama diimbangi pada saat runtime suka static_cast, tetapi juga dan pemeriksaan keamanan yang mahal bahwa downcast benar menggunakan RTTI.
Pemeriksaan keamanan ini memungkinkan Anda untuk menanyakan apakah pointer kelas dasar adalah tipe yang diberikan saat runtime dengan memeriksa pengembalian nullptryang mengindikasikan downcast yang tidak valid.
Oleh karena itu, jika kode Anda tidak dapat memeriksa itu nullptrdan mengambil tindakan non-abort yang valid, Anda harus menggunakan static_castalih-alih pemeran dinamis.
Jika abort adalah satu-satunya tindakan yang dapat dilakukan oleh kode Anda, mungkin Anda hanya ingin mengaktifkan dynamic_castin debug builds ( -NDEBUG), dan gunakan static_castsebaliknya, mis. Seperti yang dilakukan di sini , untuk tidak memperlambat lari cepat Anda.
reinterpret_cast: tidak melakukan apa pun saat runtime, bahkan alamat tidak diimbangi. Pointer harus menunjuk tepat ke tipe yang benar, bahkan kelas dasar tidak berfungsi. Anda biasanya tidak menginginkan ini kecuali stream byte mentah terlibat.
Perhatikan contoh kode berikut:
main.cpp
#include<iostream>struct B1 {
B1(int int_in_b1): int_in_b1(int_in_b1){}virtual~B1(){}void f0(){}virtualint f1(){return1;}int int_in_b1;};struct B2 {
B2(int int_in_b2): int_in_b2(int_in_b2){}virtual~B2(){}virtualint f2(){return2;}int int_in_b2;};struct D :public B1,public B2 {
D(int int_in_b1,int int_in_b2,int int_in_d): B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d){}void d(){}int f2(){return3;}int int_in_d;};int main(){
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1,2,3};// The memory layout must support the virtual method call use case.
b2s[0]=&b2;// An upcast is an implicit static_cast<>().
b2s[1]=&d;
std::cout <<"&d "<<&d << std::endl;
std::cout <<"b2s[0] "<< b2s[0]<< std::endl;
std::cout <<"b2s[1] "<< b2s[1]<< std::endl;
std::cout <<"b2s[0]->f2() "<< b2s[0]->f2()<< std::endl;
std::cout <<"b2s[1]->f2() "<< b2s[1]->f2()<< std::endl;// Now for some downcasts.// Cannot be done implicitly// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]// dp = (b2s[0]);// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp =static_cast<D*>(b2s[0]);
std::cout <<"static_cast<D*>(b2s[0]) "<< dp << std::endl;
std::cout <<"static_cast<D*>(b2s[0])->int_in_d "<< dp->int_in_d << std::endl;// OK
dp =static_cast<D*>(b2s[1]);
std::cout <<"static_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"static_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;// Segfault because dp is nullptr.
dp =dynamic_cast<D*>(b2s[0]);
std::cout <<"dynamic_cast<D*>(b2s[0]) "<< dp << std::endl;//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;// OK
dp =dynamic_cast<D*>(b2s[1]);
std::cout <<"dynamic_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"dynamic_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;// Undefined behaviour to an unrelated memory address because this// did not calculate the offset to get from B2* to D*.
dp =reinterpret_cast<D*>(b2s[1]);
std::cout <<"reinterpret_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"reinterpret_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;}
B1:+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:+0: pointer to virtual method table of D (for B1)+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)+12: value of int_in_b2
+16: value of int_in_d
Fakta kuncinya adalah bahwa struktur data memori Dberisi di dalamnya struktur memori yang kompatibel dengan struktur internal B1dan B2internal.
Karena itu kami mencapai kesimpulan kritis:
upcast atau downcast hanya perlu menggeser nilai pointer dengan nilai yang diketahui pada waktu kompilasi
Dengan cara ini, ketika Dditeruskan ke array tipe dasar, pemeran tipe sebenarnya menghitung offset itu dan menunjukkan sesuatu yang tampak persis seperti yang valid B2dalam memori:
b2s[1]=&d;
kecuali bahwa ini memiliki vtable untuk Dbukan B2, dan karena itu semua panggilan virtual bekerja secara transparan.
Sekarang, kita akhirnya bisa kembali mengetik pengecoran dan analisis contoh konkret kami.
Dari output stdout kita melihat:
&d 0x7fffffffc930
b2s[1]0x7fffffffc940
Oleh karena itu, implisit yang static_castdilakukan di sana benar menghitung offset dari Dstruktur data lengkap pada 0x7fffffffc930 ke yang B2sejenisnya yaitu pada 0x7fffffffc940. Kami juga menyimpulkan bahwa apa yang terletak antara 0x7fffffffc930 dan 0x7fffffffc940 kemungkinan adalah B1data dan vtable.
Kemudian, pada bagian yang tertekan, sekarang mudah untuk memahami bagaimana yang tidak valid gagal dan mengapa:
static_cast<D*>(b2s[0]) 0x7fffffffc910: kompiler baru saja naik 0x10 pada waktu kompilasi byte untuk mencoba dan pergi dari B2ke yang berisiD
Tetapi karena b2s[0]bukan D, itu sekarang menunjuk ke wilayah memori yang tidak ditentukan.
Pertama ada cek NULL, dan mengembalikan NULL jika einput adalah NULL.
Kalau tidak, itu mengatur beberapa argumen dalam RDX, RSI dan RDI dan panggilan __dynamic_cast.
Saya tidak memiliki kesabaran untuk menganalisis ini lebih jauh sekarang, tetapi seperti yang orang lain katakan, satu-satunya cara agar ini bekerja adalah __dynamic_castdengan mengakses beberapa struktur data memori RTTI tambahan yang mewakili hirarki kelas.
Oleh karena itu harus dimulai dari B2entri untuk tabel itu, kemudian berjalan hirarki kelas ini sampai menemukan bahwa vtable untuk Dtypecast dari b2s[0].
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940yang ini hanya mempercayai kita secara membabi buta: kita mengatakan ada Dalamat b2s[1], dan kompiler tidak melakukan perhitungan offset.
Tapi ini salah, karena D sebenarnya di 0x7fffffffc930, apa yang ada di 0x7fffffffc940 adalah struktur seperti B2 di dalam D! Jadi sampah diakses.
Kami dapat mengkonfirmasi ini dari -O0majelis menghebohkan yang hanya memindahkan nilai sekitar:
Jawaban:
static_cast
adalah pemeran pertama yang harus Anda coba gunakan. Itu melakukan hal-hal seperti konversi implisit antara jenis (sepertiint
untukfloat
, atau penunjuk kevoid*
), dan juga dapat memanggil fungsi konversi eksplisit (atau yang implisit). Dalam banyak kasus, menyatakan secara eksplisitstatic_cast
tidak perlu, tetapi penting untuk dicatat bahwaT(something)
sintaksinya setara dengan(T)something
dan harus dihindari (lebih lanjut tentang itu nanti).T(something, something_else)
Namun, A aman dan dijamin untuk memanggil konstruktor.static_cast
juga dapat dilemparkan melalui hierarki warisan. Tidak perlu ketika casting ke atas (menuju kelas dasar), tetapi ketika casting ke bawah itu dapat digunakan selama tidak dibuang melaluivirtual
warisan. Itu tidak melakukan pengecekan, bagaimanapun, dan itu adalah perilaku tidak terdefinisi untukstatic_cast
menurunkan hierarki ke tipe yang sebenarnya bukan tipe objek.const_cast
dapat digunakan untuk menghapus atau menambahconst
variabel; tidak ada pemain C ++ lainnya yang mampu menghapusnya (bahkan tidakreinterpret_cast
). Penting untuk dicatat bahwa memodifikasiconst
nilai sebelumnya hanya tidak terdefinisi jika variabel aslinyaconst
; jika Anda menggunakannya untukconst
melepas referensi ke sesuatu yang tidak dideklarasikan dengannyaconst
, itu aman. Ini bisa bermanfaat ketika kelebihan fungsi anggota berdasarkanconst
, misalnya. Itu juga dapat digunakan untuk menambahconst
objek, seperti untuk memanggil fungsi anggota yang berlebihan.const_cast
juga bekerja dengan cara yang samavolatile
, meskipun itu kurang umum.dynamic_cast
secara eksklusif digunakan untuk menangani polimorfisme. Anda bisa melemparkan pointer atau referensi ke tipe polimorfik apa pun ke tipe kelas lain (tipe polimorfik memiliki setidaknya satu fungsi virtual, dideklarasikan atau diwarisi). Anda dapat menggunakannya untuk lebih dari sekedar melakukan casting ke bawah - Anda dapat melakukan gerakan sideways atau bahkan ke atas. Thedynamic_cast
akan mencari objek yang diinginkan dan mengembalikannya jika mungkin. Jika tidak bisa, itu akan kembalinullptr
dalam kasus penunjuk, atau melemparstd::bad_cast
dalam kasus referensi.dynamic_cast
memiliki beberapa keterbatasan. Ini tidak berfungsi jika ada beberapa objek dari jenis yang sama dalam hierarki warisan (yang disebut 'berlian yang ditakuti') dan Anda tidak menggunakanvirtual
warisan. Itu juga hanya bisa melalui warisan publik - itu akan selalu gagal untuk melakukan perjalanan melaluiprotected
atauprivate
warisan. Namun, ini jarang menjadi masalah, karena bentuk-bentuk warisan seperti itu jarang terjadi.reinterpret_cast
adalah pemain yang paling berbahaya, dan harus digunakan sangat hemat. Itu mengubah satu jenis langsung ke yang lain - seperti melemparkan nilai dari satu pointer ke yang lain, atau menyimpan pointer di dalamint
, atau segala macam hal buruk lainnya. Sebagian besar, satu-satunya jaminan yang Anda dapatkanreinterpret_cast
adalah bahwa jika Anda mengembalikan hasilnya ke jenis aslinya, Anda akan mendapatkan nilai yang sama persis (tetapi tidak jika jenis perantara lebih kecil dari jenis aslinya). Ada sejumlah konversi yangreinterpret_cast
tidak dapat dilakukan juga. Ini digunakan terutama untuk konversi aneh dan manipulasi bit, seperti mengubah aliran data mentah menjadi data aktual, atau menyimpan data dalam bit rendah dari pointer untuk menyelaraskan data.Pemain gaya-C dan pemain gaya- fungsi adalah masing-masing pemain yang menggunakan
(type)object
atautype(object)
, dan secara fungsional setara. Mereka didefinisikan sebagai yang pertama dari berikut yang berhasil:const_cast
static_cast
(meskipun mengabaikan batasan akses)static_cast
(lihat di atas), laluconst_cast
reinterpret_cast
reinterpret_cast
, kemudianconst_cast
Karena itu dapat digunakan sebagai pengganti gips lain dalam beberapa kasus, tetapi bisa sangat berbahaya karena kemampuan untuk berubah menjadi
reinterpret_cast
, dan yang terakhir harus dipilih ketika pengecoran eksplisit diperlukan, kecuali Anda yakinstatic_cast
akan berhasil ataureinterpret_cast
akan gagal . Meski begitu, pertimbangkan opsi yang lebih panjang dan lebih eksplisit.static_cast
Pemain gaya-C juga mengabaikan kontrol akses saat melakukan , yang berarti mereka memiliki kemampuan untuk melakukan operasi yang tidak bisa dilakukan oleh pemain lain. Ini sebagian besar adalah kludge, dan dalam pikiran saya hanyalah alasan lain untuk menghindari gips C-style.sumber
const
(bahkan tidakreinterpret_cast
)" ... benarkah? Bagaimana denganreinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))
?reinterpret_cast
seringkali merupakan senjata pilihan ketika berhadapan dengan set data API tipe API yang tidak jelasGunakan
dynamic_cast
untuk mengkonversi pointer / referensi dalam hierarki warisan.Gunakan
static_cast
untuk konversi jenis biasa.Gunakan
reinterpret_cast
untuk menafsirkan ulang pola bit tingkat rendah. Gunakan dengan sangat hati-hati.Gunakan
const_cast
untuk membuangconst/volatile
. Hindari ini kecuali Anda terjebak menggunakan API const-salah.sumber
(Banyak penjelasan teoretis dan konseptual telah diberikan di atas)
Di bawah ini adalah beberapa contoh praktis ketika saya menggunakan static_cast , dynamic_cast , const_cast , reinterpret_cast .
(Juga rujuk ini untuk memahami penjelasannya: http://www.cplusplus.com/doc/tutorial/typecasting/ )
static_cast:
dynamic_cast:
const_cast:
reinterpret_cast:
sumber
static_cast<char*>(&val)
?static_cast
hanya berfungsi di antara jenis dengan konversi yang ditentukan, hubungan yang terlihat menurut pewarisan, atau ke / darivoid *
. Untuk yang lainnya, ada gips lainnya.reinterpret cast
untukchar *
jenis apa pun diizinkan untuk memungkinkan membaca representasi objek apa pun - dan satu-satunya kasus di mana kata kunci tersebut berguna, bukan generator yang merajalela dari implementasi- / perilaku tidak terdefinisi. Tetapi ini tidak dianggap sebagai konversi 'normal', jadi tidak diperbolehkan oleh (biasanya) sangat konservatifstatic_cast
.Mungkin membantu jika Anda tahu sedikit tentang internal ...
static_cast
static_cast
untuk mereka.A
keB
, konstruktorstatic_cast
panggilanB
lewatA
sebagai param. Atau,A
dapat memiliki operator konversi (yaituA::operator B()
). JikaB
tidak memiliki konstruktor seperti itu, atauA
tidak memiliki operator konversi, maka Anda mendapatkan kesalahan waktu kompilasi.A*
untukB*
selalu berhasil jika A dan B berada dalam hierarki warisan (atau batal) jika tidak Anda mendapatkan kesalahan kompilasi.A&
untukB&
.dynamic_cast
(Base*)
untuk(Derived*)
dapat gagal jika pointer tidak benar-benar tipe berasal.A*
keB*
, jika casting tidak valid maka dynamic_cast akan mengembalikan nullptr.A&
keB&
jika pemain tidak valid maka dynamic_cast akan membuang pengecualian bad_cast.const_cast
set<T>
yang hanya mengembalikan elemen-elemennya sebagai const untuk memastikan Anda tidak mengubah kuncinya. Namun jika maksud Anda adalah untuk memodifikasi anggota non-kunci objek maka itu harus ok. Anda dapat menggunakan const_cast untuk menghapus constness.T& SomeClass::foo()
jugaconst T& SomeClass::foo() const
. Untuk menghindari duplikasi kode, Anda dapat menerapkan const_cast untuk mengembalikan nilai satu fungsi dari yang lain.reinterpret_cast
sumber
If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.
Anda mendapatkan UB yang dapat menghasilkan segfault saat runtime jika Anda beruntung. 2. Cor dinamis dapat juga digunakan dalam cross casting. 3. Pemain cast dapat menghasilkan UB dalam beberapa kasus. Menggunakanmutable
mungkin merupakan pilihan yang lebih baik untuk menerapkan keteguhan logis.mutable
, cross casting dll.Apakah ini menjawab pertanyaan Anda?
Saya tidak pernah menggunakan
reinterpret_cast
, dan bertanya-tanya apakah berlari ke case yang membutuhkannya bukan bau desain yang buruk. Pada basis kode yang saya kerjakan banyakdynamic_cast
digunakan. Bedanya denganstatic_cast
adalah bahwadynamic_cast
tidak pengecekan runtime yang mungkin (lebih aman) atau mungkin tidak (overhead) menjadi apa yang Anda inginkan (lihat MSDN ).sumber
reinterpret_cast
untuk mengekstrak potongan data dari array. Sebagai contoh jika saya memilikichar*
buffer besar yang penuh dengan data biner yang saya butuhkan untuk bergerak dan mendapatkan primitif individu dari berbagai jenis. Sesuatu seperti ini:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
reinterpret_cast
, tidak ada banyak kegunaan untuk itu.reinterpret_cast
digunakan untuk satu alasan. Saya telah melihat data objek mentah disimpan ke tipe data "gumpalan" dalam database, kemudian ketika data tersebut diambil dari database,reinterpret_cast
digunakan untuk mengubah data mentah ini menjadi objek.Selain jawaban lain sejauh ini, berikut adalah contoh yang tidak jelas di mana
static_cast
tidak cukup sehinggareinterpret_cast
diperlukan. Misalkan ada fungsi yang dalam parameter output mengembalikan pointer ke objek dari kelas yang berbeda (yang tidak berbagi kelas basis umum). Contoh nyata dari fungsi tersebut adalahCoCreateInstance()
(lihat parameter terakhir, yang sebenarnyavoid**
). Misalkan Anda meminta kelas objek tertentu dari fungsi ini, jadi Anda tahu sebelumnya jenis untuk pointer (yang sering Anda lakukan untuk objek COM). Dalam hal ini Anda tidak dapat memasukkan pointer ke pointer Andavoid**
denganstatic_cast
: you needreinterpret_cast<void**>(&yourPointer)
.Dalam kode:
Namun,
static_cast
berfungsi untuk pointer sederhana (bukan pointer ke pointer), sehingga kode di atas dapat ditulis ulang untuk menghindarireinterpret_cast
(dengan harga variabel tambahan) dengan cara berikut:sumber
&static_cast<void*>(pNetFwPolicy2)
bukanstatic_cast<void**>(&pNetFwPolicy2)
?Sementara jawaban lain menggambarkan dengan baik semua perbedaan antara cast C ++, saya ingin menambahkan catatan singkat mengapa Anda tidak harus menggunakan cast C-style
(Type) var
danType(var)
.Untuk pemain C ++ pemula C-style terlihat seperti menjadi operasi superset lebih dari C ++ gips (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) dan seseorang dapat lebih memilih mereka daripada pemain C ++ . Sebenarnya pemeran C-style adalah superset dan lebih pendek untuk menulis.
Masalah utama para pemain C-style adalah mereka menyembunyikan niat sebenarnya dari para pemain. Gips C-style dapat melakukan hampir semua jenis gips dari gips yang biasanya aman dilakukan oleh static_cast <> () dan dynamic_cast <> () ke gips yang berpotensi berbahaya seperti const_cast <> (), di mana pengubah konst dapat dihapus sehingga variabel konst dapat dimodifikasi dan reinterpret_cast <> () yang bahkan dapat menafsirkan kembali nilai integer ke pointer.
Ini contohnya.
Alasan utama mengapa para pemain C ++ ditambahkan ke dalam bahasa ini adalah untuk memungkinkan pengembang untuk mengklarifikasi niatnya - mengapa ia akan melakukan pemeran itu. Dengan menggunakan gips C-style yang benar-benar valid di C ++ Anda membuat kode Anda kurang mudah dibaca dan lebih rentan kesalahan terutama untuk pengembang lain yang tidak membuat kode Anda. Jadi untuk membuat kode Anda lebih mudah dibaca dan eksplisit, Anda harus selalu lebih suka cast C ++ daripada cast gaya-C.
Berikut adalah kutipan pendek dari buku Bjarne Stroustrup (penulis C ++) The C ++ Programming Language edisi ke 4 - halaman 302.
sumber
Untuk memahami, mari pertimbangkan potongan kode di bawah ini:
Hanya baris (4) yang dikompilasi tanpa kesalahan. Hanya reinterpret_cast yang dapat digunakan untuk mengonversi pointer ke objek ke pointer ke jenis objek apa pun yang tidak terkait.
Satu hal yang perlu diperhatikan adalah: dynamic_cast akan gagal pada saat run-time, tetapi pada kebanyakan kompiler, ia juga akan gagal untuk dikompilasi karena tidak ada fungsi virtual dalam struct dari pointer yang sedang di-cast, artinya dynamic_cast hanya akan bekerja dengan pointer kelas polymorphic saja .
Kapan menggunakan C ++ cast :
sumber
static_cast
vsdynamic_cast
vsreinterpret_cast
internal melihat pada downcast / upcastDalam jawaban ini, saya ingin membandingkan tiga mekanisme ini pada contoh konkret / downcast konkret dan menganalisis apa yang terjadi pada pointer / memori / perakitan yang mendasari untuk memberikan pemahaman konkret tentang bagaimana mereka membandingkan.
Saya percaya bahwa ini akan memberikan intuisi yang baik tentang bagaimana para pemain berbeda:
static_cast
: apakah satu alamat offset pada saat runtime (dampak runtime rendah) dan tidak ada keamanan yang memeriksa bahwa downcast benar.dyanamic_cast
: apakah alamat yang sama diimbangi pada saat runtime sukastatic_cast
, tetapi juga dan pemeriksaan keamanan yang mahal bahwa downcast benar menggunakan RTTI.Pemeriksaan keamanan ini memungkinkan Anda untuk menanyakan apakah pointer kelas dasar adalah tipe yang diberikan saat runtime dengan memeriksa pengembalian
nullptr
yang mengindikasikan downcast yang tidak valid.Oleh karena itu, jika kode Anda tidak dapat memeriksa itu
nullptr
dan mengambil tindakan non-abort yang valid, Anda harus menggunakanstatic_cast
alih-alih pemeran dinamis.Jika abort adalah satu-satunya tindakan yang dapat dilakukan oleh kode Anda, mungkin Anda hanya ingin mengaktifkan
dynamic_cast
in debug builds (-NDEBUG
), dan gunakanstatic_cast
sebaliknya, mis. Seperti yang dilakukan di sini , untuk tidak memperlambat lari cepat Anda.reinterpret_cast
: tidak melakukan apa pun saat runtime, bahkan alamat tidak diimbangi. Pointer harus menunjuk tepat ke tipe yang benar, bahkan kelas dasar tidak berfungsi. Anda biasanya tidak menginginkan ini kecuali stream byte mentah terlibat.Perhatikan contoh kode berikut:
main.cpp
Kompilasi, jalankan, dan bongkar dengan:
mana
setarch
yang digunakan untuk ASLR menonaktifkan untuk membuatnya lebih mudah untuk membandingkan berjalan.Output yang mungkin:
Sekarang, seperti yang disebutkan di: https://en.wikipedia.org/wiki/Virtual_method_table untuk mendukung panggilan metode virtual secara efisien, struktur data memori
D
harus terlihat seperti:Fakta kuncinya adalah bahwa struktur data memori
D
berisi di dalamnya struktur memori yang kompatibel dengan struktur internalB1
danB2
internal.Karena itu kami mencapai kesimpulan kritis:
Dengan cara ini, ketika
D
diteruskan ke array tipe dasar, pemeran tipe sebenarnya menghitung offset itu dan menunjukkan sesuatu yang tampak persis seperti yang validB2
dalam memori:kecuali bahwa ini memiliki vtable untuk
D
bukanB2
, dan karena itu semua panggilan virtual bekerja secara transparan.Sekarang, kita akhirnya bisa kembali mengetik pengecoran dan analisis contoh konkret kami.
Dari output stdout kita melihat:
Oleh karena itu, implisit yang
static_cast
dilakukan di sana benar menghitung offset dariD
struktur data lengkap pada 0x7fffffffc930 ke yangB2
sejenisnya yaitu pada 0x7fffffffc940. Kami juga menyimpulkan bahwa apa yang terletak antara 0x7fffffffc930 dan 0x7fffffffc940 kemungkinan adalahB1
data dan vtable.Kemudian, pada bagian yang tertekan, sekarang mudah untuk memahami bagaimana yang tidak valid gagal dan mengapa:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: kompiler baru saja naik 0x10 pada waktu kompilasi byte untuk mencoba dan pergi dariB2
ke yang berisiD
Tetapi karena
b2s[0]
bukanD
, itu sekarang menunjuk ke wilayah memori yang tidak ditentukan.Pembongkarannya adalah:
jadi kita melihat bahwa GCC tidak:
D
yang tidak adadynamic_cast<D*>(b2s[0]) 0
: C ++ sebenarnya menemukan bahwa para pemain tidak valid dan dikembalikannullptr
!Tidak ada cara ini dapat dilakukan pada waktu kompilasi, dan kami akan mengkonfirmasi itu dari pembongkaran:
Pertama ada cek NULL, dan mengembalikan NULL jika einput adalah NULL.
Kalau tidak, itu mengatur beberapa argumen dalam RDX, RSI dan RDI dan panggilan
__dynamic_cast
.Saya tidak memiliki kesabaran untuk menganalisis ini lebih jauh sekarang, tetapi seperti yang orang lain katakan, satu-satunya cara agar ini bekerja adalah
__dynamic_cast
dengan mengakses beberapa struktur data memori RTTI tambahan yang mewakili hirarki kelas.Oleh karena itu harus dimulai dari
B2
entri untuk tabel itu, kemudian berjalan hirarki kelas ini sampai menemukan bahwa vtable untukD
typecast darib2s[0]
.Inilah sebabnya mengapa menafsirkan ulang pemain berpotensi mahal! Berikut adalah contoh di mana tambalan satu liner yang mengubah a
dynamic_cast
menjadistatic_cast
dalam proyek kompleks mengurangi runtime sebesar 33%! .reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
yang ini hanya mempercayai kita secara membabi buta: kita mengatakan adaD
alamatb2s[1]
, dan kompiler tidak melakukan perhitungan offset.Tapi ini salah, karena D sebenarnya di 0x7fffffffc930, apa yang ada di 0x7fffffffc940 adalah struktur seperti B2 di dalam D! Jadi sampah diakses.
Kami dapat mengkonfirmasi ini dari
-O0
majelis menghebohkan yang hanya memindahkan nilai sekitar:Pertanyaan-pertanyaan Terkait:
Diuji pada Ubuntu 18.04 amd64, GCC 7.4.0.
sumber