Menggunakan Union dalam C untuk Pengemasan dan Penguraian Data yang Efisien
Menggunakan Union dalam C untuk Pengemasan dan Penguraian Data yang Efisien
Union memberikan cara yang kuat dan efisien dalam memori untuk menafsirkan byte yang sama sebagai tipe data berbeda. Meskipun awalnya dimaksudkan untuk memori bersama antara variabel yang saling eksklusif, programmer C modern sering menggunakan union untuk pengemasan data, penguraian data, dan type punning—terutama saat menangani protokol serial atau komunikasi tingkat rendah.
Bagaimana Union Berbagi Memori
Semua anggota union berbagi wilayah memori yang sama. Anggota terbesar menentukan ukuran union, dan setiap anggota dimulai di alamat yang sama.
union { uint16_t word; struct { uint8_t byte1; uint8_t byte2; }; } u1;
Di sini, word menempati dua byte, sementara struktur bersarang membagi dua byte tersebut menjadi byte1 dan byte2. Tata letak dapat divisualisasikan sebagai:

Mengemas Dua Byte ke dalam Kata 16-bit
Untuk menggabungkan dua nilai 8-bit x dan y menjadi kata 16-bit, tetapkan mereka ke anggota struktur dan kemudian baca anggota word:
u1.byte1 = y; u1.byte2 = x; uint16_t packed = u1.word;
“Data punning” ini sederhana tetapi bergantung pada urutan byte prosesor.
Endianness Proesor Penting
Sistem little-endian menyimpan byte paling tidak signifikan terlebih dahulu, sedangkan sistem big-endian menyimpan byte paling signifikan terlebih dahulu. Pertimbangkan urutan 0x01020304:

Menjalankan tes sederhana pada mesin little-endian menghasilkan:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word is: %#X\n", u1.word); return 0; }
Output: Word is: 0X4321—mengonfirmasi penyimpanan little-endian. Pada target big-endian, kode yang sama akan menghasilkan 0X2143.
Alternatif Bitwise: Pengemasan Independen Endianness
Penggeseran dan masking memberikan cara portabel untuk membangun nilai 16-bit:
uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4);
Membandingkan kedua metode:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word1; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word1 is: %#X\n", u1.word1); uint8_t byte3 = 0x21, byte4 = 0x43; uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4); printf("Word2 is: %#X\n", word2); return 0; }
Pada prosesor big-endian: Word1 is: 0X2143, Word2 is: 0X2143. Pada prosesor little-endian: Word1 is: 0X4321, Word2 is: 0X2143. Pendekatan bitwise menjamin hasil yang konsisten di seluruh arsitektur.
Contoh Praktis: Mengirim Float melalui UART
Tautan serial biasanya mentransmisikan satu byte setiap kali. Untuk mengirim float 32-bit, kita dapat menggunakan union untuk melihat float sebagai array empat byte:
union buffer { float f; struct { uint8_t byte[4]; }; };
Sisi transmisi:
union buffer tx; union buffer rx; float f1 = 5.5f; tx.f = f1; for (int i = 0; i < 4; ++i) { // send tx.byte[i] over UART rx.byte[i] = tx.byte[i]; // Simulated receive } printf("The received data is: %f\n", rx.f);
Output: The received data is: 5.500000. Union memisahkan representasi byte dari nilai floating-point dengan rapi, membuat kode mudah dibaca dan dirawat.

Kesimpulan
- Union memungkinkan type punning, tetapi hasilnya bergantung pada endianness.
- Operator bitwise menyediakan pengemasan yang portabel dan independen arsitektur.
- Untuk kejelasan dan pemeliharaan, banyak pengembang masih mengutamakan union dalam konteks sistem tertanam.
- Union unggul saat mentransmisikan struktur data sembarang melalui tautan berorientasi byte.
Untuk menjelajahi lebih lanjut tentang C, union, dan sistem tertanam, jelajahi daftar lengkap artikel saya.