Saya mendapat kesan bahwa mengakses union
anggota selain dari set terakhir adalah UB, tetapi saya sepertinya tidak dapat menemukan referensi yang kuat (selain jawaban yang mengklaim itu adalah UB tetapi tanpa dukungan dari standar).
Jadi, apakah itu perilaku yang tidak terdefinisi?
c++
undefined-behavior
language-lawyer
unions
Luchian Grigore
sumber
sumber
Jawaban:
Kebingungannya adalah bahwa C secara eksplisit memungkinkan jenis-menghukum melalui serikat pekerja, sedangkan C ++ (c ++ 11) tidak memiliki izin seperti itu.
Situasi dengan C ++:
C ++ nantinya memiliki bahasa yang mengizinkan penggunaan serikat yang berisi
struct
s dengan urutan awal umum; Namun ini tidak mengizinkan hukuman jenis.Untuk menentukan apakah jenis serikat hukuman yang diperbolehkan dalam C ++, kita harus mencari lebih lanjut. Ingat ituc99 adalah referensi normatif untuk C ++ 11 (dan C99 memiliki bahasa yang mirip dengan C11 yang memungkinkan penghukuman jenis serikat):
Ini menjadi sangat menarik ketika kita membaca
Jadi untuk tipe primitif (yang ipso facto memiliki inisialisasi sepele) yang terkandung dalam persatuan, umur objek mencakup setidaknya masa perserikatan itu sendiri. Ini memungkinkan kita untuk memohon
Dengan asumsi bahwa operasi yang kami minati adalah tipe-punning yaitu mengambil nilai anggota serikat yang tidak aktif, dan diberikan sesuai dengan di atas bahwa kami memiliki referensi yang valid ke objek yang dirujuk oleh anggota tersebut, operasi itu bernilai rendah untuk -nilai konversi:
Pertanyaannya kemudian adalah apakah suatu objek yang merupakan anggota serikat tidak aktif diinisialisasi dengan penyimpanan ke anggota serikat aktif. Sejauh yang saya tahu, ini tidak terjadi dan meskipun jika:
char
penyimpanan array dan kembali (3.9: 2), atauakses ke persatuan oleh anggota tidak aktif didefinisikan dan didefinisikan untuk mengikuti representasi objek dan nilai, akses tanpa salah satu dari interposisi di atas adalah perilaku yang tidak terdefinisi. Ini memiliki implikasi untuk optimisasi yang diperbolehkan untuk dilakukan pada program seperti itu, karena implementasi tentu saja dapat mengasumsikan bahwa perilaku yang tidak terdefinisi tidak terjadi.
Yaitu, meskipun kita dapat secara sah membentuk nilai untuk anggota serikat yang tidak aktif (itulah sebabnya menugaskan anggota yang tidak aktif tanpa konstruksi boleh saja) itu dianggap tidak diinisialisasi.
sumber
memcpy
implementasi kustom (mengakses objek menggunakan nilai-unsigned char
nilai), ia melarang akses ke*p
setelahint *p = 0; const int *const *pp = &p;
(meskipun konversi implisit dariint**
keconst int*const*
valid), ia melarang bahkan mengaksesc
setelahstruct S s; const S &c = s;
. Masalah CWG 616 . Apakah kata-kata baru itu memungkinkan? Ada juga [basic.lval].&
operator unary ketika diterapkan pada anggota serikat. Saya akan berpikir pointer yang dihasilkan harus dapat digunakan untuk mengakses anggota setidaknya sampai waktu berikutnya langsung atau tidak langsung menggunakan nilai anggota lain, tetapi dalam gcc pointer tidak dapat digunakan bahkan selama itu, yang menimbulkan pertanyaan tentang apa yang&
operator seharusnya berarti.Standar C ++ 11 mengatakan seperti ini
Jika hanya satu nilai yang disimpan, bagaimana Anda bisa membaca yang lain? Itu tidak ada.
Dokumentasi gcc mencantumkan ini di bawah Perilaku yang ditentukan implementasi
menunjukkan bahwa ini tidak diperlukan oleh standar C.
2016-01-05: Melalui komentar saya ditautkan dengan C99 Defect Report # 283 yang menambahkan teks yang mirip dengan catatan kaki ke dokumen standar C:
Tidak yakin apakah itu menjelaskan banyak, mengingat catatan kaki tidak normatif untuk standar.
sumber
Saya pikir yang paling mendekati standar untuk mengatakan itu perilaku tidak terdefinisi adalah di mana ia mendefinisikan perilaku untuk serikat pekerja yang berisi urutan awal umum (C99, §6.5.2.3 / 5):
C ++ 11 memberikan persyaratan / izin yang sama di §9.2 / 19:
Meskipun tidak ada yang menyatakan secara langsung, keduanya membawa implikasi yang kuat bahwa "memeriksa" (membaca) anggota "diizinkan" hanya jika 1) itu adalah (bagian dari) anggota yang paling baru ditulis, atau 2) merupakan bagian dari inisial umum urutan.
Itu bukan pernyataan langsung bahwa melakukan sebaliknya adalah perilaku yang tidak terdefinisi, tetapi yang paling dekat yang saya sadari.
sumber
union
yang tidak terdefinisi, karena saya mendapat kesan oleh blog tertentu bahwa ini tidak masalah, dan membangun beberapa struktur besar dan proyek di sekitarnya. Sekarang saya pikir saya mungkin baik-baik saja, karena kelas sayaunion
memang berisi kelas yang memiliki tipe yang sama di depanunion
mengandung misalnya sebuahuint8_t
danclass Something { uint8_t myByte; [...] };
- saya akan berasumsi ketentuan ini juga akan berlaku di sini, tapi itu bernada sangat sengaja hanya memungkinkanstruct
s. Untungnya saya sudah menggunakan yang bukan primitif mentah: OSesuatu yang belum disebutkan oleh jawaban yang tersedia adalah catatan kaki 37 dalam paragraf 21 bagian 6.2.5:
Persyaratan ini tampaknya secara jelas menyiratkan bahwa Anda tidak boleh menulis di anggota dan membaca yang lain. Dalam hal ini mungkin perilaku yang tidak terdefinisi dengan kurangnya spesifikasi.
sumber
Saya jelaskan ini dengan sebuah contoh.
menganggap kita memiliki kesatuan berikut:
Saya berasumsi bahwa
sizeof(int)
memberi 4, dan itusizeof(short)
memberi 2.ketika Anda menulis dengan
union A a = {10}
baik buat var baru tipe A di dalamnya nilai 10.memori Anda akan terlihat seperti itu: (ingat bahwa semua anggota serikat pekerja mendapatkan lokasi yang sama)
seperti yang Anda lihat, nilai kapak adalah 10, nilai ay 1 adalah 10, dan nilai ay [0] adalah 0.
sekarang, apa yang terjadi jika saya melakukan ini?
ingatan kita akan terlihat seperti ini:
ini akan mengubah nilai kapak menjadi 2424842 (dalam desimal).
sekarang, jika serikat Anda memiliki float, atau double, peta memori Anda menjadi lebih berantakan, karena cara Anda menyimpan angka pastinya. info lebih lanjut bisa Anda dapatkan di sini .
sumber