Aturan pengetikan kuat pada Ada melarang konversi antar tipe yang tidak saling terkait, dan aturan itu melindungi sebagian besar program. Pada lapisan terbawah, sebagian pekerjaan membutuhkan pengecualian yang disengaja, misalnya saat menafsirkan bit mentah dari buffer protokol atau membebaskan memori yang dialokasikan melalui tipe akses. Bahasa menyediakan dua operasi unchecked untuk keperluan itu, yaitu generic Unchecked_Conversion untuk konversi tipe dan generic Unchecked_Deallocation untuk pembebasan memori. Keduanya disebut unchecked karena melewati pemeriksaan bahasa, sehingga penggunaannya harus disengaja, terdokumentasi, dan dibatasi pada lapisan yang memang membutuhkannya.
Unchecked_Conversion untuk penafsiran bit#
Instansiasi generic Unchecked_Conversion menghasilkan fungsi yang mengubah tipe sumber menjadi tipe tujuan tanpa menghitung apa pun. Bit-bit pada nilai sumber disalin apa adanya, kemudian dibaca seakan-akan bertipe tujuan, sehingga hasilnya disebut penafsiran (reinterpretation) terhadap pola bit yang sama. Bentuk instansiasinya menyerupai instansiasi generic lain, yaitu menyebut tipe sumber dan tipe tujuan secara eksplisit pada parameter Source dan Target.
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
procedure Konversi_Bertanda is
type Kata_32 is mod 2**32;
function Kata_Ke_Integer is new Ada.Unchecked_Conversion (
Source => Kata_32,
Target => Integer);
Nilai : constant Kata_32 := 2**31;
Tampilan : constant Integer := Kata_Ke_Integer (Nilai);
begin
Ada.Text_IO.Put_Line ("Kata_32 = " & Kata_32'Image (Nilai));
Ada.Text_IO.Put_Line ("Integer = " & Integer'Image (Tampilan));
end Konversi_Bertanda;# kompilasi program
gnatmake -q konversi_bertanda.adb
# jalankan program
./konversi_bertandaKata_32 = 2147483648
Integer = -2147483648Nilai 2**31 pada tipe modular 32 bit dan nilai negatif pada Integer menempati pola bit yang sama. Tampilannya berbeda karena tipe tujuan menafsirkan bit tertinggi sebagai penanda bilangan negatif, sebagaimana kelaziman representasi bilangan bertanda. Pola penafsiran semacam ini berguna ketika data masuk dari jaringan atau perangkat dalam bentuk kata mentah, sedangkan sisa program mengharuskan tampilan bertanda yang lazim.
Instansiasi hanya menambah sedikit teks pada deklarasi, tetapi konsekuensinya besar, yaitu kompilator berhenti memeriksa kesesuaian antara tipe sumber dan tipe tujuan. Karena itu, konversi semacam ini disertai komentar yang menyebut alasannya, dan penggunaannya dibatasi pada satu package antarmuka, misalnya package pengolah bingkai protokol. Dengan pembatasan itu, sisa program tetap menggunakan tipe yang sudah diperiksa, sedangkan penafsiran bit hanya terjadi di satu titik yang mudah diaudit.
Pemeriksaan ukuran pada saat kompilasi#
Konversi unchecked paling aman apabila kedua tipe berukuran sama, dan kompilator menyampaikan ketidakcocokan tersebut melalui warning pada saat kompilasi. Warning ini muncul sebelum program dijalankan, sehingga kesalahan ukuran tertangkap lebih dahulu. Program berikut sengaja memasangkan tipe modular 8 bit dengan tipe modular 32 bit untuk memperlihatkan perilaku warning tersebut.
with Ada.Unchecked_Conversion;
procedure Ukuran_Cocok is
type Pendek is mod 2**8;
type Panjang is mod 2**32;
function Salah_Ukur is new Ada.Unchecked_Conversion (Pendek, Panjang);
begin
null;
end Ukuran_Cocok;# kompilasi ulang program untuk melihat warning ukuran
gnatmake -q -f ukuran_cocok.adbukuran_cocok.adb:7:04: warning: types for unchecked conversion have different sizes [-gnatwz]Pesan warning tersebut menyebut baris instansiasi beserta sakelar [-gnatwz] yang mengendalikannya, dan GNAT mengaktifkannya secara default tanpa konfigurasi tambahan. Kompilasi tetap berakhir dengan sukses, tetapi hasil konversinya tidak dapat diandalkan karena bit sumber dipotong atau ditambah. Warning semacam ini diatasi dengan menyamakan ukuran kedua tipe atau dengan menetapkan atribut Size secara eksplisit. Perbaikan yang paling langsung adalah memasangkan dua tipe dengan ukuran yang sama, misalnya dua tipe modular 32 bit. Apabila ukuran yang berbeda memang disengaja, ukuran salah satu tipe dinyatakan dengan klausul Size sehingga kompilator tidak lagi menemukan ketidakcocokan. Keputusan itu tetap dicatat dengan komentar agar penafsiran bitnya mudah ditelusuri kembali.
Unchecked_Deallocation untuk pembebasan memori#
Objek yang dialokasikan dengan alokator new menempati memori hingga program berakhir, kecuali dipbebaskan secara eksplisit. Prosedur generik Unchecked_Deallocation menghasilkan prosedur pembebasan untuk setiap pasangan tipe objek dan tipe penunjuknya, dan penunjuk yang dibebaskan bernilai null setelah pemanggilan. Instansiasi diberi nama prosedur tersendiri, misalnya Bebaskan, agar setiap panggilannya terbaca sebagai keputusan yang disengaja. Instansiasi ini diletakkan di samping deklarasi tipe penunjuknya agar setiap tipe akses memiliki satu prosedur pembebasan yang jelas.
with Ada.Text_IO;
with Ada.Unchecked_Deallocation;
procedure Bebas_Memori is
type Rekaman_Suhu is record
Nilai : Float;
Jumlah_Sampel : Natural;
end record;
type Penunjuk_Suhu is access Rekaman_Suhu;
procedure Bebaskan is new Ada.Unchecked_Deallocation (
Object => Rekaman_Suhu,
Name => Penunjuk_Suhu);
Data : Penunjuk_Suhu := new Rekaman_Suhu'(Nilai => 24.5, Jumlah_Sampel => 3);
begin
Ada.Text_IO.Put_Line ("nilai awal = " & Float'Image (Data.Nilai));
Bebaskan (Data);
Ada.Text_IO.Put_Line ("Data = null setelah dealokasi = "
& Boolean'Image (Data = null));
end Bebas_Memori;# kompilasi program
gnatmake -q bebas_memori.adb
# jalankan program
./bebas_memorinilai awal = 2.45000E+01
Data = null setelah dealokasi = TRUEKeluaran menunjukkan dua hal, yaitu objek terbaca normal sebelum dibebaskan dan penunjuk Data bernilai null setelah pemanggilan Bebaskan. Memori objek dikembalikan kepada pool penyimpanan tempat objek itu dialokasikan, sehingga dapat digunakan kembali oleh alokasi berikutnya. Tanpa pembebasan, program yang mengalokasikan berulang kali, misalnya satu rekaman bacaan per detik, akan menghabiskan memori secara perlahan.
Kesalahan umum#
Empat kekeliruan berikut paling sering muncul saat operasi unchecked dipraktikkan.
- Mengakses objek melalui penunjuk kedua setelah pembebasan. Penunjuk yang tersisa tidak bernilai null dan tetap menyimpan alamat lama. Batasi kepemilikan objek pada satu penunjuk, atau beri nilai null pada penunjuk lain secara eksplisit sebelum membebaskan objek.
- Menganggap Unchecked_Conversion sebagai konversi aritmetika. Fungsi ini tidak menghitung apa pun, melainkan menafsir ulang bit, sehingga pembulatan atau penggeseran nilai tidak pernah terjadi di dalamnya. Konversi yang benar-benar menghitung nilai dilakukan dengan konversi tipe biasa, misalnya
Float (X). - Membiarkan warning ukuran tanpa tindak lanjut. Konversi antar tipe yang berbeda ukuran menghasilkan bit yang terpotong atau berlebih, dan nilai tujuannya tidak dapat diandalkan. Samakan ukuran kedua tipe sebelum konversi digunakan.
- Membebaskan penunjuk yang belum pernah diinisialisasi. Pembebasan pada penunjuk null berlalu tanpa efek, tetapi pembebasan pada alamat acak merusak pool penyimpanan. Inisialisasi setiap penunjuk dengan null atau dengan hasil alokasi yang jelas.
Ringkasan#
- Instansiasi Unchecked_Conversion menghasilkan fungsi yang menafsir ulang pola bit tanpa menghitung, sedangkan instansiasi Unchecked_Deallocation menghasilkan prosedur yang membebaskan memori dan menetapkan penunjuknya menjadi null.
- Kompilator memperingatkan ketidakcocokan ukuran kedua tipe pada Unchecked_Conversion secara default pada saat kompilasi.
- Operasi unchecked digunakan secara sengaja pada lapisan yang membutuhkannya, dengan batasan kepemilikan penunjuk yang jelas dan inisialisasi null pada setiap penunjuk.
Referensi#
- 13.9 Unchecked Type Conversions — Ada Reference Manual 2022 — Menetapkan generic Unchecked_Conversion dan aturan ukurannya.
- 13.11.2 Unchecked Storage Deallocation — Ada Reference Manual 2022 — Menetapkan prosedur generik Unchecked_Deallocation beserta efeknya pada penunjuk.
- Access Types — Advanced Ada, AdaCore — Bab kursus lanjutan yang membahas manajemen memori dan Unchecked_Deallocation.