Saya ingin menyimpan tipe data campuran dalam array. Bagaimana seseorang bisa melakukan itu?
c
arrays
variant
mixed-type
chanzerre
sumber
sumber
Jawaban:
Anda dapat membuat elemen-elemen array sebagai kesatuan yang terdiskriminasi, alias yang ditandai .
The
type
anggota digunakan untuk menyimpan pilihan yang anggotaunion
yang harus digunakan untuk setiap elemen array. Jadi, jika Anda ingin menyimpanint
elemen pertama, Anda harus:Ketika Anda ingin mengakses elemen array, Anda harus terlebih dahulu memeriksa jenisnya, lalu menggunakan anggota serikat yang sesuai. Sebuah
switch
pernyataan berguna:Terserah kepada programmer untuk memastikan bahwa
type
anggota selalu sesuai dengan nilai terakhir yang disimpan diunion
.sumber
Gunakan serikat pekerja:
Anda harus melacak tipe setiap elemen.
sumber
Elemen array harus memiliki ukuran yang sama, itu sebabnya itu tidak mungkin. Anda bisa mengatasinya dengan membuat jenis varian :
Ukuran elemen penyatuan adalah ukuran elemen terbesar, 4.
sumber
Ada gaya berbeda dalam mendefinisikan tag-union (dengan nama apa pun) yang membuat IMO jauh lebih baik untuk digunakan , dengan menghapus union internal. Ini adalah gaya yang digunakan dalam Sistem X Window untuk hal-hal seperti Acara.
Contoh dalam jawaban Barmar memberi nama
val
pada persatuan internal. Contoh dalam jawaban Sp. menggunakan serikat anonim untuk menghindari keharusan menentukan.val.
setiap kali Anda mengakses catatan varian. Sayangnya, struktur dan serikat internal "anonim" tidak tersedia di C89 atau C99. Ini adalah ekstensi kompiler, dan karenanya bersifat non-portabel.Cara IMO yang lebih baik adalah membalikkan seluruh definisi. Buat setiap data mengetik struct sendiri, dan menempatkan tag (type specifier) ke dalam masing-masing struct.
Kemudian Anda bungkus ini dalam serikat tingkat atas.
Sekarang mungkin terlihat bahwa kita mengulangi diri kita sendiri, dan memang begitu . Tetapi pertimbangkan bahwa definisi ini cenderung terisolasi untuk satu file. Tapi kami telah menghilangkan suara dari menentukan perantara
.val.
sebelum Anda sampai ke data.Sebaliknya, itu terjadi pada akhirnya, di mana itu kurang menjengkelkan. : D
Hal lain yang memungkinkan adalah bentuk warisan. Sunting: bagian ini bukan standar C, tetapi menggunakan ekstensi GNU.
Up-casting dan down-casting.
Sunting: Satu hal yang perlu diperhatikan adalah jika Anda membuat salah satunya dengan inisialisasi C99 yang ditunjuk. Semua inisialisasi anggota harus melalui anggota serikat yang sama.
The
.tag
initializer dapat diabaikan oleh compiler mengoptimalkan, karena.int_
initializer yang mengikuti alias area data yang sama. Meskipun kita tahu tata letak (!), Dan itu harusnya ok. Tidak, bukan itu. Gunakan tag "internal" sebagai gantinya (itu menutupi tag luar, seperti yang kita inginkan, tetapi tidak membingungkan kompiler).sumber
.int_.val
tidak alias area yang sama karena kompiler tahu bahwa.val
ada di offset yang lebih besar daripada.tag
. Apakah Anda punya tautan ke diskusi lebih lanjut tentang masalah yang dituduhkan ini?Anda dapat melakukan
void *
larik, dengan larik terpisahsize_t.
Tapi Anda kehilangan tipe informasinya.Jika Anda perlu menyimpan tipe informasi dengan cara tertentu, simpan array int ketiga (di mana int adalah nilai yang disebutkan) Kemudian kode fungsi yang digunakan tergantung pada
enum
nilainya.sumber
Serikat pekerja adalah cara standar untuk maju. Tetapi Anda memiliki solusi lain juga. Salah satunya adalah penunjuk yang ditandai , yang melibatkan penyimpanan lebih banyak informasi dalam bit "bebas" dari penunjuk.
Bergantung pada arsitektur, Anda dapat menggunakan bit rendah atau tinggi, tetapi cara teraman dan paling portabel adalah menggunakan bit rendah yang tidak digunakan dengan mengambil keuntungan dari memori selaras. Misalnya dalam sistem 32-bit dan 64-bit, pointer ke
int
harus kelipatan 4 (dengan asumsiint
adalah tipe 32-bit) dan 2 bit paling tidak signifikan harus 0, maka Anda dapat menggunakannya untuk menyimpan jenis nilai Anda . Tentu saja Anda perlu menghapus bit tag sebelum menentukan pointer. Misalnya jika tipe data Anda terbatas pada 4 jenis yang berbeda maka Anda dapat menggunakannya seperti di bawah iniJika Anda dapat memastikan bahwa data selaras 8-byte (seperti untuk pointer dalam sistem 64-bit, atau
long long
danuint64_t
...), Anda akan memiliki satu bit lagi untuk tag.Ini memiliki satu kelemahan yaitu Anda akan membutuhkan lebih banyak memori jika data belum disimpan dalam variabel di tempat lain. Oleh karena itu jika jenis dan jangkauan data Anda terbatas, Anda dapat menyimpan nilai-nilai secara langsung di pointer. Teknik ini telah digunakan dalam versi 32-bit dari mesin V8 Chrome , di mana ia memeriksa bit paling tidak signifikan dari alamat untuk melihat apakah itu adalah penunjuk ke objek lain (seperti bilangan bulat, bilangan bulat besar, string atau beberapa objek) atau 31 -ditandatangani nilai (disebut
smi
- integer kecil ). Jika itu adalahint
, Chrome hanya melakukan aritmatika dengan menggeser 1 bit untuk mendapatkan nilai, jika tidak, penunjuk akan ditinjau ulang.Pada kebanyakan sistem 64-bit saat ini, ruang alamat virtual masih jauh lebih sempit daripada 64 bit, oleh karena itu bit yang paling signifikan juga dapat digunakan sebagai tag . Bergantung pada arsitekturnya, Anda memiliki berbagai cara untuk menggunakannya sebagai tag. ARM , 68k , dan banyak lainnya dapat dikonfigurasi untuk mengabaikan bit teratas , memungkinkan Anda untuk menggunakannya secara bebas tanpa khawatir tentang segfault atau apa pun. Dari artikel Wikipedia yang ditautkan di atas:
Pada x86_64 Anda masih dapat menggunakan bit tinggi sebagai tag dengan hati-hati . Tentu saja Anda tidak perlu menggunakan semua 16 bit itu dan dapat meninggalkan beberapa bit untuk bukti di masa depan
Dalam versi sebelumnya Mozilla Firefox mereka juga menggunakan optimisasi integer kecil seperti V8, dengan 3 bit rendah yang digunakan untuk menyimpan tipe (int, string, objek ... dll). Tetapi sejak JägerMonkey mereka mengambil jalur lain ( Representasi Nilai JavaScript Baru Mozilla , tautan cadangan ). Nilai sekarang selalu disimpan dalam variabel presisi ganda 64-bit. Ketika
double
adalah dinormalisasi satu, dapat digunakan secara langsung dalam perhitungan. Namun jika 16 bit tinggi semuanya 1s, yang menunjukkan NaN , 32 bit rendah akan menyimpan alamat (di komputer 32-bit) ke nilai atau nilai secara langsung, 16-bit sisanya akan digunakan untuk menyimpan tipenya. Teknik ini disebut NaN-tinjuatau nun-tinju. Ini juga digunakan dalam JavaScriptCore WebKit 64-bit dan SpiderMonkey Mozilla dengan pointer disimpan dalam bit 48 yang rendah. Jika tipe data utama Anda adalah floating-point, ini adalah solusi terbaik dan memberikan kinerja yang sangat baik.Baca lebih lanjut tentang teknik di atas: https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
sumber