Kebutuhan menyimpan banyak pilihan ya/tidak dalam satu tempat muncul pada kendali perangkat, format berkas, dan pengaturan tampilan. bit field menyusun anggota struct dalam satuan bit, sehingga beberapa flag muat dalam satu unit memori. Topik ini membahas pendefinisian bit field beserta batas nilainya, catatan portabilitasnya, serta flexible array member dari C99 yang melengkapi struct dengan data berukuran bebas di ujungnya.
Menyusun flag dengan bit field#
Bit field ditulis seperti anggota biasa dengan tambahan tanda titik dua dan jumlah bitnya. Tipe dasarnya unsigned int, dan kompiler menempatkan bit-bit itu berdampingan di dalam satu unit memori. Tiga bit menampung nilai berbeda, sehingga penanda berjenjang seperti kode warna tetap muat dalam ruang yang sama dengan flag ya/tidak.
#include <stdio.h>
struct GayaTeks {
unsigned int tebal : 1;
unsigned int miring : 1;
unsigned int garis_bawah : 1;
unsigned int kode_warna : 3;
};
int main(void) {
struct GayaTeks g = {0};
unsigned char *byte = (unsigned char *)&g;
g.tebal = 1;
g.kode_warna = 5;
printf("tebal=%u miring=%u garis_bawah=%u kode_warna=%u\n",
g.tebal, g.miring, g.garis_bawah, g.kode_warna);
printf("sizeof(struct GayaTeks) = %zu\n", sizeof(struct GayaTeks));
printf("byte pertama = %u\n", byte[0]);
return 0;
}tebal=1 miring=0 garis_bawah=0 kode_warna=5
sizeof(struct GayaTeks) = 4
byte pertama = 41Struct GayaTeks menampung tiga flag ya/tidak dan satu kode warna tiga bit dalam 4 byte, yaitu ukuran satu unsigned int. Anggota kode_warna bernilai 5 sesuai penugasannya, sedangkan anggota yang tidak diisi bernilai 0 karena struct diinisialisasi {0}. Byte pertama terbaca 41. Nilai itu terdiri atas bit tebal bernilai 1 pada posisi paling kanan dan pola 101 milik kode_warna, yaitu . Nilai yang melebihi kapasitas bitnya terpotong, misalnya penugasan 9 pada bit field tiga bit menyisakan 9 mod 8 atau 1.
Unit penampung bit field mengikuti tipe dasarnya, sehingga ukuran struct yang seluruh anggotanya bit field berupa kelipatan ukuran unsigned int pada gcc. Penambahan satu anggota bit field tidak serta-merta memperbesar struct selama masih ada bit kosong di unit yang sama, dan ukurannya baru bertambah ketika unit tambahan diperlukan. Bit field yang dinyatakan dengan lebar 0, misalnya unsigned int : 0;, memaksa anggota berikutnya dimulai pada unit baru, dan bentuk ini jarang dibutuhkan pada tingkat pengenalan.
Demo berikut memperlihatkan susunan bit itu secara langsung. Ubah pilihan flag dan kode warnanya, lalu amati pola bit serta nilai desimal yang tersimpan.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simulator bit gaya teks</title>
<style>
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 14px 18px;
background: #ffffff;
color: #222222;
}
h2 {
font-size: 15px;
margin: 0 0 10px;
}
.kendali {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-bottom: 14px;
}
.kendali label {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
}
.kendali select {
font-size: 14px;
padding: 2px 4px;
}
.baris-bit {
display: flex;
gap: 4px;
align-items: flex-end;
}
.sel-bit {
width: 44px;
height: 44px;
border: 1.5px solid #888888;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
background: #f2f2f2;
color: #999999;
}
.sel-bit.hidup {
background: #cfe2ff;
border-color: #4472c4;
color: #1a3a6b;
}
.label-bit {
width: 44px;
font-size: 11px;
text-align: center;
color: #555555;
margin-top: 4px;
}
.kolom {
display: flex;
flex-direction: column;
align-items: center;
}
#hasil {
margin-top: 14px;
font-size: 15px;
line-height: 1.7;
}
#hasil code {
background: #f4f4f4;
padding: 1px 5px;
border-radius: 3px;
}
</style>
</head>
<body>
<h2>struct GayaTeks pada byte pertamanya</h2>
<div class="kendali">
<label><input type="checkbox" id="tebal"> tebal</label>
<label><input type="checkbox" id="miring"> miring</label>
<label><input type="checkbox" id="garis"> garis_bawah</label>
<label>kode_warna
<select id="warna">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5" selected>5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
</label>
</div>
<div class="baris-bit" id="baris-bit"></div>
<div class="baris-bit" id="baris-label"></div>
<div id="hasil"></div>
<script>
var NAMA_BIT = ["tebal", "miring", "garis_bawah", "", "", "", "kosong", "kosong"];
function nilaiByte() {
var tebal = document.getElementById("tebal").checked ? 1 : 0;
var miring = document.getElementById("miring").checked ? 1 : 0;
var garis = document.getElementById("garis").checked ? 1 : 0;
var warna = parseInt(document.getElementById("warna").value, 10);
return tebal | (miring << 1) | (garis << 2) | (warna << 3);
}
function gambar() {
var nilai = nilaiByte();
var barisBit = document.getElementById("baris-bit");
var barisLabel = document.getElementById("baris-label");
barisBit.innerHTML = "";
barisLabel.innerHTML = "";
for (var bit = 7; bit >= 0; bit = bit - 1) {
var sel = document.createElement("div");
var hidup = ((nilai >> bit) & 1) === 1;
sel.className = "sel-bit" + (hidup ? " hidup" : "");
sel.textContent = hidup ? "1" : "0";
barisBit.appendChild(sel);
var label = document.createElement("div");
label.className = "label-bit";
label.textContent = NAMA_BIT[bit] === "" ? "kode" : NAMA_BIT[bit];
barisLabel.appendChild(label);
}
var biner = nilai.toString(2).padStart(8, "0");
document.getElementById("hasil").innerHTML =
"Byte pertama = <code>" + biner + "</code> (biner) = <code>" +
nilai + "</code> (desimal)";
}
document.getElementById("tebal").addEventListener("change", gambar);
document.getElementById("miring").addEventListener("change", gambar);
document.getElementById("garis").addEventListener("change", gambar);
document.getElementById("warna").addEventListener("change", gambar);
gambar();
</script>
</body>
</html>Byte pertama pada demo tersusun dari bit paling kanan sebagai tebal, dua bit berikutnya sebagai miring dan garis_bawah, tiga bit untuk kode_warna, dan dua bit teratas tidak terpakai. Susunan persisnya ditentukan kompiler, dan demo ini mengikuti tata letak gcc pada program di atas supaya angkanya saling menguatkan.
Batas dan portabilitas bit field#
Bit field memiliki beberapa batas teknis. Alamat satu bit tidak dapat diambil, sehingga operator & ditolak kompiler apabila ditujukan kepada anggota bit field. Urutan bit dan pemilihan unit penampungnya juga bergantung kompiler, sehingga tata letaknya tidak dijamin sama antarplatform. Bit field dipakai untuk menghemat memori di dalam satu program, dan bukan untuk format data yang harus identik lintas mesin.
Bit field boleh bercampur dengan anggota biasa di dalam satu struct. Anggota biasa ditempatkan pada lokasi tersendiri sesuai aturan perataannya, sedangkan kumpulan bit field mengisi unit penampungnya secara berdampingan. Apabila struct semacam ini perlu dikirim atau disimpan sebagai rangkaian byte, susunannya kembali bergantung platform. Fungsi pengemasnya harus ditulis secara eksplisit, dan bukan sekadar menyalin memori struct itu.
Array fleksibel di ujung struct#
Standar C99 mengizinkan anggota array tanpa ukuran di akhir struct, yang disebut flexible array member. Anggota ini tidak dihitung oleh sizeof struct, tetapi datanya dapat ditempatkan tepat setelah struct dalam satu blok memori. Pola ini menyatukan kepala data dan isinya, sehingga cukup satu kali alokasi dan satu kali pembebasan. Fungsi malloc memesan blok memori sebesar argumennya dan mengembalikan alamat blok tersebut, sedangkan free membebaskan blok yang dipesan. Keduanya dibahas lengkap pada modul Memori Dinamis.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Pesan {
int panjang;
char teks[];
};
int main(void) {
struct Pesan *p = malloc(sizeof(struct Pesan) + 13);
if (p == NULL) {
return 1;
}
p->panjang = 12;
strcpy(p->teks, "Catatan hari");
printf("sizeof(struct Pesan) = %zu\n", sizeof(struct Pesan));
printf("panjang = %d\n", p->panjang);
printf("teks = %s\n", p->teks);
free(p);
return 0;
}sizeof(struct Pesan) = 4
panjang = 12
teks = Catatan harisizeof(struct Pesan) melaporkan 4 byte karena anggota teks[] tidak ikut dihitung. Alokasinya meminta 4 byte kepala ditambah 13 byte teks untuk 12 huruf dan penanda akhir '\0'. Setelah teks diisi, struct terbaca sebagai satu kesatuan yang kepalanya menyatu dengan isinya. Pola ini lazim pada pesan jaringan dan entri berkas dengan panjang yang beragam.
Kesalahan umum#
Menandai bit field dengan tipe bertanda menimbulkan kejutan. Bit field int flag : 1 hanya menampung 0 dan -1 pada gcc karena satu bitnya habis untuk tanda, sehingga penugasan 1 terbaca sebagai -1. Gunakan unsigned int untuk flag, kecuali nilai negatif memang dimaksudkan.
Kekeliruan kedua adalah mengalokasikan array fleksibel tanpa ruang penanda akhir. String 12 huruf memerlukan 13 byte, sehingga alokasi yang hanya menambah 12 byte menulis melewati batas bloknya. Hitung panjangnya dengan strlen ditambah satu bila isinya berupa string, lalu jumlahkan ke ukuran struct pada saat memanggil malloc.
Ringkasan#
- Bit field menetapkan jumlah bit untuk setiap anggota sehingga beberapa flag muat dalam satu unit memori.
- Nilai yang melebihi kapasitas bitnya terpotong, dan alamat anggota bit field tidak dapat diambil dengan operator &.
- Tata letak bit bergantung kompiler dan platform, sehingga bit field bukan sarana format data lintas mesin.
- Flexible array member menambahkan data berukuran bebas di ujung struct untuk dialokasikan bersama dalam satu blok.
Referensi#
- Bit-field — Cppreference — Kaidah bit field, batas pemakaiannya, dan catatan portabilitas.
- Struct, union, enumerated types — Cppreference — Aturan flexible array member sejak C99 pada bagian anggota struct.
- malloc — Cppreference — Kaidah alokasi memori yang dipakai pola array fleksibel.