Struct packed dan extern

Mengendalikan tata letak memori struct dengan packed untuk susunan bit dan extern untuk ABI C.

Daftar isi

Struct biasa pada Zig menyerahkan penempatan field kepada kompilator, dan tata letaknya dapat berubah antar versi. Untuk pekerjaan protokol, berkas biner, dan pengembangan sistem operasi, susunan byte dan bit harus dipatok secara eksplisit. Zig menyediakan dua kualifikasi untuk itu, yaitu extern struct yang menjanjikan tata letak ABI C, dan packed struct yang mengemas field berurutan tanpa padding hingga tingkat bit. Topik ini menguji kedua tata letak tersebut dengan pengukuran ukuran dan offset, lalu menuntaskan konversi dua arah antara struct dan bilangan bulat.

Konsep#

extern struct menjamin tata letak yang sama dengan yang dihasilkan kompilator C. Field ditempatkan mengikuti urutan deklarasi dengan aturan alignment C, sehingga padding muncul di tempat yang sama dengan yang akan dihasilkan C, dan struct semacam ini sah dikirim ke atau dari kode C tanpa konversi. Ukurannya diperiksa dengan @sizeOf dan posisi setiap field diperiksa dengan @offsetOf.

packed struct menempatkan field berurutan mulai dari bit paling rendah tanpa satu pun padding. Tipe fieldnya harus punya lebar yang jelas, dan lebar totalnya menjadi lebar struct, diukur dengan @bitSizeOf bukan @sizeOf karena tidak ada byte penuh yang disisipkan. Struktur bit ini adalah dasar untuk membaca dan menulis protokol tingkat bit, misalnya header paket jaringan yang membagi satu byte menjadi beberapa field.

Urutan bitnya dapat dibaca dari contoh kelak, yaitu field pertama menempati bit ke-nol dan field berikutnya naik ke arah bit yang lebih tinggi. Kaidah penempatan ini menegaskan mengapa nilai bendera pada contoh menghasilkan pola 0x15, bukan kebalikannya. Karena tidak ada padding, dua packed struct berlebar total yang sama dapat dikonversi antar tipenya dengan @bitCast tanpa risiko kehilangan informasi, selama lebar bitnya identik.

Konversi antara packed struct dan bilangan bulat berlangsung dengan @bitCast, dan berlaku dua arah selama total lebitnya sama. Konversi inilah yang mengubah struct menjadi byte yang dikirim melalui jaringan atau disimpan ke berkas, kemudian dibaca kembali dengan tepat.

Contoh Lengkap#

Program berikut memuat sebuah extern struct yang meniru header protokol dan sebuah packed struct untuk byte bendera, lalu mencetak ukuran, offset, dan pola bitnya.

zig layout-protokol.zig
const std = @import("std");

const Header = extern struct {
    tanda: u16,
    panjang: u32,
    saluran: u8,
};

const Bendera = packed struct {
    aktif: bool,
    ulang: bool,
    prioritas: u3,
    cadangan: u3,
};

pub fn main() void {
    std.debug.print("ukuran Header: {d} byte\n", .{@sizeOf(Header)});
    std.debug.print("offset tanda: {d}\n", .{@offsetOf(Header, "tanda")});
    std.debug.print("offset panjang: {d}\n", .{@offsetOf(Header, "panjang")});
    std.debug.print("offset saluran: {d}\n", .{@offsetOf(Header, "saluran")});
    std.debug.print("lebar Bendera: {d} bit\n", .{@bitSizeOf(Bendera)});

    const bendera: Bendera = .{ .aktif = true, .ulang = false, .prioritas = 5, .cadangan = 0 };
    const bit: u8 = @bitCast(bendera);
    std.debug.print("pola bit: 0x{X:0>2}\n", .{bit});

    const kembali: Bendera = @bitCast(bit);
    std.debug.print("aktif: {}, prioritas: {d}\n", .{ kembali.aktif, kembali.prioritas });
}
bash
zig run layout-protokol.zig
text Keluaran
ukuran Header: 12 byte
offset tanda: 0
offset panjang: 4
offset saluran: 8
lebar Bendera: 8 bit
pola bit: 0x15
aktif: true, prioritas: 5

Bacaan pada Header memperlihatkan kerja alignment C. Field tanda berukuran dua byte berada pada offset 0, tetapi panjang yang berukuran empat byte mulai pada offset 4, bukan 2, karena membutuhkan sejajar empat byte. Dua byte padding mengisi celahnya, dan saluran menempati offset 8 sehingga ukuran total menjadi 12 byte setelah padding akhir. Angka-angka ini identik dengan yang dihasilkan kompilator C untuk deklarasi yang sama.

Bacaan pada Bendera memperlihatkan pengemasan bit. aktif menempati bit ke-0, ulang bit ke-1, dan prioritas tiga bit berikutnya. Nilai aktif 1 dan prioritas 5 menghasilkan pola 0x15, yaitu biner 00010101, dan konversi balik dengan @bitCast mengembalikan field dengan nilai yang sama. Satu byte menyimpan empat field sekaligus tanpa kehilangan kejelasan tipe, dan perhitungannya terbaca langsung, yaitu aktif menyumbang 1, ulang nol, serta prioritas bernilai 5 dikalikan empat sehingga totalnya 21 atau 0x15.

Kedua kualifikasi tersebut juga boleh bertingkat. extern struct boleh memuat field packed struct, dan pola ini lazim pada header protokol yang menyimpan byte bendera di dalam struktur ber-ABI C. Susunannya tetap dapat diperiksa dengan @offsetOf untuk field luar dan @bitCast untuk byte benderanya, sehingga pengujian tata letak tetap berjalan tanpa alat eksternal.

Kesalahan umum#

Kesalahan kedua berupa mengharapkan struct biasa memiliki tata letak yang stabil. Struct tanpa kualifikasi bebas menyusun ulang field dan menambah padding sesuai keputusan kompilator, sehingga menyimpannya ke berkas dengan @bitCast menghasilkan data yang hanya dapat dibaca oleh build yang sama. Aturan yang dipatok selalu ditulis eksplisit, yaitu extern untuk ABI C dan packed untuk susunan bit.

Kesalahan ketiga berupa @bitCast antar lebar yang berbeda. Konversi dari Bendera berlebar 8 bit ke u16 ditolak dengan pesan error kompilasi invalid cast to ..., karena @bitCast menuntut total lebar bit yang sama di kedua sisi. Perbaikannya berupa memperluas tipe tujuan terlebih dahulu dengan konversi biasa, misalnya u8 ke u16, sebelum konversi bit yang sesungguhnya.

Ringkasan#

  • extern struct mematok tata letak ABI C, lengkap dengan alignment dan padding-nya, dan diukur dengan @sizeOf serta @offsetOf.
  • packed struct mengemas field berurutan hingga tingkat bit tanpa padding, dan lebarnya diukur dengan @bitSizeOf.
  • @bitCast mengubah packed struct menjadi bilangan bulat dan sebaliknya, dengan syarat lebar bit yang sama.
  • Tata letak yang dipatok adalah prasyarat membaca dan menulis format biner secara andal.

Referensi#