Struct dan Union

Kali ini, kita akan mempelajari tentang type data struct dan union. Saya membahas keduanya dalam satu judul karena adanya kesamaan. Sebelum kalian belajar tentang struct dan union, pastikan kalau kalian sudah mempelajari tentang deklarasi variabel.

Tidak seperti array yang merupakan kumpulan data/variabel dengan type data yang sama, struct dan union merupakan kumpulan data/variabel dengan type data yang p berbeda-beda. Kedua tipe data tersebut tidak menggunakan indeks karena setiap anggotanya memiliki nama dan tipe datanya masing-masing.

Seperti halnya variabel dengan type data lain, struct dan union juga harus dideklarasikan sebelum digunakan.

Berikut ini adalah contoh deklarasi tipe data struct.
struct nama_struct{
tipe_data anggota_ke_1;
    tipe_data anggota_ke_2;
    //....
    tipe_data anggota_ke_n;
}nama_variabel;

Lalu, dibawah ini adalah deklarasi tipe data union.
union nama_union{
tipe_data anggota_ke_1;
    tipe_data anggota_ke_2;
    //....
    tipe_data anggota_ke_n;
}nama_variabel;

Dari sini, kalian bisa melihat kalau beda deklarasi struct dan union cuma ada di "awalnya". Perbedaan lainnya akan kalian lihat dari cara kedua tipe menyimpan nilai. Untuk lebih jelasnya, langsung saja dipraktekkan.

Struct
Struct adalah tipe data yang digunakan untuk menyimpan lebih dari satu nilai. Tidak seperti array yang mengakses nilai di dalamnya berdasarkan indeks, struct mengakses nilai-nilai yang disimpan dalam variabel berdasarkan nama yang dimiliki anggotanya. Setelah deklarasi, penulisan nama struct dan anggotanya dipisahkan dengan tanda titik, misalnya "nama_struct.nama_anggota".

Tiap anggota struct menyimpan nilai di lokasi yang berbeda beda, dan juga dengan nama yang berbeda-beda. Satu variabel dengan tipe data struct bisa memiliki beberapa anggota yang ditentukan saat deklarasi struct.

Kalau kalian pusing dengan teorinya, lebih baik coba langsung kode programnya. Selanjutnya, coba ketik contoh kode program di bawah ini!
#include <iostream>

struct kelompok_bilangan{
    float pecahan;
    int bulat;
}bilangan;

using namespace std;

int main(){
    bilangan.bulat=7;
    bilangan.pecahan=7;
    cout << "nilai awal bilangan bulat = " << bilangan.bulat << endl;
    cout << "nilai awal bilangan pecahan = " << bilangan.pecahan << endl;
    bilangan.bulat/=2;
    bilangan.pecahan/=2;
    cout << "====================================" << endl;
    cout << "nilai bilangan bulat setelah dibagi 2= " << bilangan.bulat << endl;
    cout << "nilai bilangan pecahan setelah dibagi 2= " << bilangan.pecahan << endl;
    return 0;
}
Pada program di atas kita mendeklarasikan struct dengan nama bilangan. Struct bilangan memiliki dua anggota yaitu bulat dangan tipe data "int" dan pecahan dengan type data "float". Pada contoh di atas, "kelompok_bilangan" sama seperti nama baru untuk tipe data yang dideklarasikan dengan "typedef".

Ingat! Anggota struct dan nama struct dipisahkan dengan tanda titik (.) saat digunakan. Struct tidak perlu menggunakan "typedef" karena deklarasi struct pada dasarnya menggunakan nama struct sebagai nama tipe data.

Salah satu kegunaan struct adalah untuk mengurangi parameter pada function, selain itu struct bisa digunakan untuk linked list. Untuk penggunaan struct dalam function, perhatikan parameter function dalam kode program di bawah ini!
#include <stdio.h>

using namespace std;

struct kelompok_bilangan{
    float pecahan;
    int bulat;
};

struct kelompok_bilangan bilangan;

void setengah(struct kelompok_bilangan angka){
    angka.bulat/=2;
    angka.pecahan/=2;
    printf("nilai bilangan bulat setelah dibagi 2 = %d\n", angka.bulat);
    printf("nilai bilangan pecahan setelah dibagi 2 = %0.2f\n", angka.pecahan);
}

int main(){
    bilangan.bulat=7;
    bilangan.pecahan=7;
    setengah(bilangan);

    printf("=========================================\n");
    printf("nilai awal bilangan bulat 2 = %d\n", bilangan.bulat);
    printf("nilai awal bilangan pecahan = %0.2f\n", bilangan.pecahan);

    return 0;
}
Contoh kode program di atas menggunakan kelompok_bilangan sebagai nama structnya. Nama struct sama dengan nama tipe data. Karena itu, kita tidak bisa menggunakan "nama struct" untuk menyimpan nilai.

Sebaiknya, parameter dengan tipe data struct dalam function dideklarasikan sebagai pointer. Karena jika tidak dideklarasikan sebagai pointer, struct akan disalin seluruh nilai datanya saat pemanggilan fungsi.
#include <stdio.h>
struct kelompok_bilangan{
    float pecahan;
    int bulat;
}bilangan;

void setengah(struct kelompok_bilangan *angka){
    angka->bulat/=2;
    angka->pecahan/=2;
    printf("nilai bilangan bulat setelah dibagi 2 = %d\n", angka->bulat);
    printf("nilai bilangan pecahan setelah dibagi 2 = %f\n",angka->pecahan);
}

int main(){
    bilangan.bulat=7;
    bilangan.pecahan=7;
    setengah(&bilangan);
    printf("============================================\n");
    printf("apakah %d nilai awal bilangan bulat?\n", bilangan.bulat);
    printf("apakah %f nilai awal bilangan pecahan?\n", bilangan.pecahan);
    printf("bukan. Nilai anggota struct berubah di dalam sekaligus di luar function.\n");
    return 0;
} 
Jika kalian menggunakan pointer sebagai parameter function, maka hanya alamat dari variabelnya yang akan di salin saat pemanggilan function. Selain itu, nilai dari tiap anggota struct seharusnya akan berubah juga di luar function setelah diubah di dalam function. Untuk menggunakan struct sebagai pointer, nama struct dan nama anggotanya dipisahkan dengan "->".
Pada dua contoh sebelumnya, bilangan adalah nama variabel yang akan kita berikan nilai. Jika kita ingin membuat beberapa variabel untuk satu struct yang sama, kita bisa mendeklarasikannya dengan cara seperti di bawah ini.
#include <stdio.h>

struct kelompok_bilangan{
    float pecahan;
    int bulat;
};

struct kelompok_bilangan angka1, angka2;

int main(){
    angka1.bulat=7;
    angka1.pecahan=7.3;
    angka2.bulat=3;
    angka2.pecahan=3.5;
    printf("angka1.bulat / angka2.bulat = %d\n", angka1.bulat / angka2.bulat);
    printf("angka1.pecahan / angka2.pecahan = %f\n", angka1.pecahan /angka2.pecahan);
    return 0;
}
Pada kode program di atas kita mendeklarasikan variabel angka1 dan angka2 dengan type data struct yang didefinisikan dengan nama type kelompok bilangan. Kita tidak harus menuliskan deklarasinya dalam satu baris tapi kita bisa juga menuliskannya dalam baris yang terpisah.

Union
Selain menggunakan array dan struct, kalian bisa menggunakan union untuk mengelompokkan sekumpulan variabel. Union adalah kumpulan data yang anggotanya disimpan di dalam memori yang sama dan saling mempengaruhi satu sama lain. Berbeda dengan struct yang tiap anggotanya menyimpan nilai secara terpisah, tiap anggota union menyimpan nilainya di dalam lokasi memori yang sama. Karena penyimpanannya sama dan saling timpa, maka saat nilai satu anggota diubah, nilai anggota union lain akan berubah walaupun tidak menjadi sama.
#include <stdio.h>

union kelompok_bilangan{
    unsigned short bulat1;
    unsigned long bulat2;
};

kelompok_bilangan angka;

int main(){
    angka.bulat1=256*256-1;
    printf("angka.bulat1 = %d\n", angka.bulat1);
    angka.bulat2=256*256*256;
    printf("angka.bulat2 = %ld\n", angka.bulat2);
    return 0;
}
Perhatikan perbedaan outputnya dengan kode program dibawah ini!
#include <stdio.h>

union kelompok_bilangan{
    unsigned short bulat1;
    unsigned long bulat2;
};

kelompok_bilangan angka;

int main(){
    angka.bulat1=256*256-1;
    angka.bulat2=256*256*256;
    printf("angka.bulat1 = %d\n", angka.bulat1);
    printf("angka.bulat2 = %d\n", angka.bulat2);
    return 0;
}
Kalian bisa melihat perbedaan nilai "angka.bulat1"contoh kode program yang pertama dan yang kedua. Ini bisa terjadi karena sifat union dan perbedaan penempatan function printf.

Dari contoh di atas, kita bisa menganalisa perubahan pada satu anggota union yang dapat mempengaruhi anggota lainnya. Karena sifatnya ini, union lebih sering digunakan untuk penyimpanan data sementara untuk tipe data yang berbeda-beda. Dengan union, ukuran dari memori yang digunakan bisa jadi lebih kecil untuk member dengan tipe dan jumlah yang sama.

Jika kalian ingin nilainya tidak berubah saat anggota lainnya diubah, deklarasikan kumpulan data sebagai struct jangan deklarasikan sebagai union.
#include <stdio.h>

struct kelompok_bilangan{
    unsigned short bulat1;
    unsigned long bulat2;
};

kelompok_bilangan angka;

int main(){
    angka.bulat1=256*256-1;
    angka.bulat2=256*256*256;
    printf("angka.bulat1 = %d\n", angka.bulat1);
    printf("angka.bulat2 = %d\n", angka.bulat2);
    return 0;
}

Hasil dari contoh terakhir ini sama dengan contoh pertama karena lokasi memori yang digunakan untuk tiap angggota pada struct terpisah. Saat nilai anggota kedua pada struct diubah, struct tidak mengubah nilai anggota pertama atau yang lainnya, karena tiap anggota disimpan dengan alamat memori yang benar-benar terpisah.

Isi dari union akan saling timpa karena memori yang digunakan titik awalnya memang sama. Selain itu, union cuma menggunakan memori yang ukurannya sesuai dengan ukuran data terbesar yang dimiliki anggotanya.
Coba analogikan struct dan union dengan ember atau cangkir kopi!

Apa yang kalian lakukan kalau ember yang tersedia hanya ada 4 sedangkan kalian diminta untuk mengambil 5 ember air?

Tentu saja, kalian akan mengambil 4 ember sebelum melanjutkannya dengan mengambil 1 ember air lagi. Ini akan berbeda kalau kalian punya 5 ember.

Anggap saja union seperti 4 ember tersebut, dan air adalah nilai yang diberikan. Air yang lama perlu dituang jika kalian ingin mengambil air yang baru. Kalau kalian punya lima ember, kalian bisa menggunakannya secara bersamaan tanpa perlu menuangkan air yang lama.

Bit Fields
Bit fields adalah pembatasan ukuran variabel dengan ukuran byte tertentu. Ukuran variabel dengan bit fields bisa ditulis setelah tanda titik dua (:) dalam struct atau union.

struct {
   unsigned int panjang ;
   unsigned int lebar;
};


Jika contoh di atas menggunakan 8 byte (4+4), maka contoh di bawah ini hanya menggunakan 2 byte.
struct {
   unsigned int panjang : 1;
   unsigned int lebar : 1;
};