Lompat ke konten Lompat ke sidebar Lompat ke footer

Anggota Structure, Padding, dan Data Packing Bahasa C

Pada artikel ini akan dipelajari tentang data alignment, structure packing dan padding pada Bahasa C.


Sebelum memahami lebih dalam materi tentang Anggota Structure, Padding, dan Data Packing Bahasa C, terlebih dahulu pelajari materi tentang: Struct Hack Bahasa C dan Penjelasannya, Union Bahasa C dan Penjelasannya, dan Struct Bahasa C dan Penjelasannya.

Contoh: Perkirakan hasil output dari contoh program berikut.

#include <stdio.h>


// Alignment requirements

// (typical 32 bit machine)


// char 1 byte

// short int 2 bytes

// int 4 bytes

// double 8 bytes


// struct A


typedef struct structa_tag

{

char c;

short int s;

} structa_t;



// Struct B

typedef struct structb_tag

{

short int s;

char c;

int i;

} structb_t;


// Struct C

typedef struct structc_tag

{

char c;

double d;

int s;

} structc_t;


// Struct D

typedef struct structd_tag

{

double d;

int s;

char c;

} structd_t;


int main()

{


printf("sizeof(structa_t) = %lu\n",

sizeof(structa_t));


printf("sizeof(structb_t) = %lu\n",

sizeof(structb_t));


printf("sizeof(structc_t) = %lu\n",

sizeof(structc_t));


printf("sizeof(structd_t) = %lu\n",

sizeof(structd_t));


return 0;

}


Data AlignmentSetiap tipe data pada Bahasa C dan C++ selalu memiliki persaraan alignment pada penggunaannya. Sebuah prosesor akan memproses nilai panjang huruf sama seperti nilai ukuran data bus-nya. Pada komputer dengan prosesor 32, maka proses ukuran kata akan mengambil nilai memori sebesar 4 bytes.

Awalnya, memori merupakan alamat byte yang diatur secara sekuensial. Jika memori yang telah diatur sebagai sebuah bank atau penampung tunggal dari satu buah lebar byte, maka prosesor membutuhkan hal tersebut untuk untuk mengeluarkan 4 siklus baca memori untuk mengambil integer. Hal ini jauh lebih efisien untuk membaca semua ukuran 4 bytes integer pada perputaran memori yang digunakan. Untuk lebih jauh, suatu memori akan diatur dalam suatu kelompok yang terdiri dari 4 buah penampung.


Suatu alamat memori akan selalu diurutkan secara sekuensial, jika nilai penampung adalah 0 yang menempati alamat X, maka penampung 1, penampung 2, dan penampung 3 akan berada di alamat (X + 1), (X + 2) dan (X + 3). Jika integer 4 byte dialokasikan pada alamat X yang merupakan kelipatan dari nilai 4, maka prosesor hanya memerlukan satu siklus memori untuk membaca seluruh bilangan bulat. Sedangkan, jika bilangan bulat dialokasikan pada alamat selain kelipatan 4, maka bilangan bulat tersebut akan membentang di dua baris penampung, dimana tipe integer tersebut membutuhkan dua siklus baca memori untuk mengambil data.

Sebuah variabel data alignmt berhadapan dengan sebuah jalur data yang disimpan pada penampung tersebut. Contoh, nilai perataan natural dari int pada komputer 32-bit adalah 4 bytes. Ketika sebuah tipe data secara alami telah diratakan, maka CPU akan melakukan fetch pada nilai perputaran minimum.

Hal tersebut sama halnya dengan nilai perataan alami dari short int sebesar 2 bytes. Hal ini berarti bahwa sebuah short int dapat disimpan pada penampung 0 – penampung bank 1 atau penampung 2 dan atau penampung 3. Sebuah nilai double pada tipe data membutuhkan ukuran memori sebesar 8 bytes, atau setara dua nilai baris pada memori penampung. Setiap nilai perataan yang salah atau misalignment dari nilai double akan memaksa dua proses perputara untuk fetch data double.

Catatan: sebuah variabel dengan nilai double akan dialokasikan pada nilai memori sebesar 8 byte pada tipe prosesor 32 bit dan membutuhkan dua proses perputaran memori. Sedangkan pada prosesor tipe 64, berdasarkan nilai dari penampung, dimana variabel dengan tipe double akan dialokasikan pada nilai 8 byte dan hanya membutuhkan satu kali proses perputaran baca memori.

Structure PaddingPada bahasa C dan C++ sebuah structure digunakan untuk melakukan pembungkusan data atau data pack. Hal ini tidak menyediakan proses enkapsulasi data apapun atau penyembunyian fitur data apapun, sedangkan pada C++ terjadi pengecualian terhadap nilai semantik yang sama dengan class. Karena persyaratan nilai alignment dari verbagai tipe ata, maka setiap anggota struct harus secara alami telah diratakan, dimana anggota dari struct dialokasikan urut secara meningkat.

Untuk kenyamanan, asumsikan setiap tipe struct dari variabel dialokasikan ke tipe ukuran 4 byte seperti 0x0000), dan seterusnya. Nilai dasar alamat dari struct kemudian dilipatgandakan dari angga 4, walaupun sifatnya tidak selalu dibutuhkan.

Structure A

Elemen pertama structa_t first adalah tipe char yang merupakan one byte perataan, yang diikuti dengan nilai short int. short int adalah 2 byte nilai perataan. Jika nilai elemen hort int elemen segera dialokasikan setelah nilai elemen char, maka hal tersebut akan dimulai pada nilai alamat pada angka ganjil. Kompilator kemudian akan memasukkan sebuah padding byte setelah nilai char untuk memastikan bahwa nilai short int akan memiliki nilai alamat berkalilipat dari kelipatan 2. Nilai total dari structa_t merupakan ukuran dari sizeof(char) + 1 (padding) + sizeof(short), 1 + 1 + 2 = 4 bytes.

Structure B

Anggota pertama dari structb_t adalah short int yang diikuti oleh nilai char. Karena char dapat berada pada batas byte apa pun, maka tidak diperlukan padding di antara nilai short int dan char, sehingga totalnya menempati nilai 3 byte. Anggota berikutnya adalah int. Jika int dialokasikan secara segera, maka hal tersebut akan dimulai pada batas byte ganjil. Dibutuhkan 1 byte padding setelah anggota char untuk membuat alamat anggota int berikutnya selaras dengan nilai 4 byte. Secara total, structb_t membutuhkan 2 + 1 + 1 (padding) + 4 = 8 byte.

Structure C – Setiap struct akan memiliki nilai persyaratan alignment

Menerapkan analisis yang sama, structc_t membutuhkan sizeof (char) + 7 byte padding + sizeof (double) + sizeof (int) = 1 + 7 + 8 + 4 = 20 byte. Namun, sizeof (structc_t) akan menjadi 24 byte. Oleh karena itu, bersama dengan anggota struct, variabel tipe struct juga akan memiliki keselarasan alami. 

Contoh, dideklarasikan array structc_t seperti yang ditunjukkan berikut:
structc_t structc_array[3];

Asumsikan alamat dasar structc_array adalah 0x0000 untuk perhitungan yang mudah. Jika structc_t menempati 20 (0x14) byte seperti yang dihitung, maka elemen array structc_t kedua (diindeks pada 1) akan berada pada 0x0000 + 0x0014 = 0x0014. Ini adalah alamat awal elemen array indeks 1. Anggota double dari structc_t ini akan dialokasikan pada 0x0014 + 0x1 + 0x7 = 0x001C (desimal 28) yang bukan kelipatan 8 dan bertentangan dengan persyaratan penyelarasan double. Seperti yang disebutkan sebelumnya, bahwa persyaratan penyelarasan double adalah 8 byte.

Untuk menghindari ketidaksejajaran seperti hal tersebut, maka kompilator akan memperkenalkan persyaratan penyelarasan ke setiap struct yang digunakn. Dimana hal ini akan menjadi anggota struktur terbesar. Dalam kasus penyelarasan structa_t nilai yan digunakan adalah 2, structb_t adalah 4 dan structc_t adalah 8. Jika membutuhkan nested struct, maka ukuran struct dalam terbesar akan menjadi penyelarasan langsung dari struktur yang lebih besar.

Dalam structc_t dari program yang dijelaskan sebelumnya, akan ada padding 4 byte setelah anggota int untuk membuat ukuran struktur kelipatan dari perataannya. Jadi, ukuran (structc_t) adalah 24 byte, dimana hal ini dapat menjamin keselarasan yang benar bahkan dalam array.

Structure D – Cara mengurangi padding

Telah dipahami bahwa penggunaan padding merupakan sesuatu yang tidak dapat dihindari pada Bahasa C. Namun ada cara untuk meminimalkan penggunaan padding tersebut pada Bahasa C, dimana Pemrogram harus mendeklarasikan anggota struktur dalam urutan peningkatan atau penurunan nilai ukurannya. Contohnya adalah structd_t yang diberikan dalam kode dengan nilai ukuran 16 byte sebagai nilai pengganti 24 byte structc_t.

Structure Packing

Terkadang merupakan suatu hal yang wajih dilakukan untuk menghindari penggunaan padding yang terdapat diantara anggota struct. Misalnya membaca isi header file ELF atau header file BMP atau JPEG. Untuk melakukan hal tersebut maka diperlukan pendefinisian struct yang mirip dengan tata letak tajuk dan memetakannya. Namun, kehati-hatian harus selalu dilakukan dalam mengakses anggota tersebut. Biasanya membaca byte demi byte adalah opsi untuk menghindari pengecualian yang tidak selaras, dimana akan ada hit pada kinerja yang dilakukan tersebut. Untuk mengatasi hal tersebut, sebagian besar kompilator menyediakan ekstensi non standar untuk mematikan padding default seperti pragma atau command line switches.

Pointer MishapsAda kemungkinan terjadinya kesalahan potensial saat menangani aritmatika pointer. Misalnya, mendereferensi pointer generik (void *) seperti yang ditunjukkan di bawah ini dapat menyebabkan pengecualian yang tidak selaras,

// Dereferensi pointer generic dengan cara tidak aman, dimana tidak terdapat jaminan bahwa pointer generic akan menghasilkan nila perataan integer.
*(int *)pGeneric;

Jika pointer pGeneric tidak sejajar sesuai persyaratan tipe data, maka ada kemungkinan untuk mendapatkan pengecualian yang tidak selaras. Faktanya beberapa prosesor tidak akan memiliki dua bit terakhir dari nilai decoding alamat, dan tidak ada cara untuk mengakses alamat yang tidak selaras, dimana prosesor akan menghasilkan pengecualian yang tidak selaras, jika pemrogram mencoba mengakses alamat tersebut.

Sebuah Catatan pada malloc() Returned Pointer

Pointer yang dikembalikan oleh malloc() adalah void *. Hal ini dapat dikonversi ke tipe data apa pun sesuai kebutuhan programmer. Pelaksana malloc() harus mengembalikan pointer yang disejajarkan dengan ukuran maksimum tipe data primitif yang telah ditentukan oleh kompilator. Biasanya disejajarkan dengan batas 8 byte pada mesin 32 bit.

Dalam dunia pemrograman C, pengelolaan memori adalah aspek yang sangat penting dalam merancang program yang efisien dan cepat. Salah satu konsep yang mendasar dalam pengelolaan memori adalah struktur data (structure), yang digunakan untuk menyimpan berbagai tipe data dalam satu entitas. Struktur data ini memungkinkan pengelompokan berbagai variabel yang memiliki tipe data berbeda menjadi satu kesatuan yang lebih terorganisir. Namun, seiring dengan penggunaan struktur data, muncul pula konsep lain yang perlu diperhatikan, seperti padding dan data packing. Kedua konsep ini mempengaruhi bagaimana memori dialokasikan dalam struktur data tersebut. 

Pada dasarnya, sebuah struktur dalam bahasa C adalah tipe data yang dapat menampung lebih dari satu elemen data yang memiliki tipe yang berbeda. Anggota dari sebuah struktur bisa berupa variabel dengan tipe data yang berbeda-beda, seperti integer, float, karakter, atau tipe data lainnya. Setiap anggota dalam struktur tersebut dapat diakses secara terpisah berdasarkan nama anggota tersebut. 

Namun, meskipun struktur memberikan kemudahan dalam pengelolaan data, cara struktur ini disusun dalam memori komputer sering kali memerlukan perhatian lebih. Dalam sebuah struktur, setiap anggota disusun berurutan dalam memori sesuai dengan urutan deklarasinya. Meskipun demikian, cara penyusunan ini dapat mempengaruhi efisiensi memori yang digunakan. Ketika struktur memiliki anggota dengan tipe data yang berbeda-beda, kompilator akan melakukan penyusunan sedemikian rupa agar akses terhadap anggota-anggota tersebut dapat dilakukan secara efisien.

Padding dalam Struktur

Padding adalah proses penambahan ruang kosong di antara anggota struktur atau di akhir struktur untuk memastikan bahwa setiap anggota struktur terletak pada alamat memori yang sesuai dengan ukuran tipe data yang diinginkan. Padding sering kali dilakukan untuk memenuhi batasan alignment yang diperlukan oleh arsitektur komputer.

Arsitektur komputer modern biasanya memiliki persyaratan alignment yang cukup ketat. Misalnya, tipe data tertentu seperti integer atau double mungkin membutuhkan alamat memori yang merupakan kelipatan dari ukuran tipe data tersebut. Sebagai contoh, sebuah integer biasanya memerlukan alamat yang merupakan kelipatan dari 4 byte, sedangkan tipe data double memerlukan alamat yang merupakan kelipatan dari 8 byte. Jika anggota struktur tidak disusun dengan mempertimbangkan alignment ini, maka akses memori menjadi kurang efisien dan dapat menyebabkan penurunan kinerja program. 

Sebagai contoh, dalam sebuah struktur yang memiliki beberapa anggota dengan ukuran tipe data yang berbeda-beda, kompilator akan menambahkan padding untuk memastikan setiap anggota terletak pada alamat yang sesuai. Sebagai ilustrasi, jika sebuah struktur memiliki anggota bertipe char yang hanya memerlukan 1 byte, tetapi anggota berikutnya bertipe integer yang memerlukan 4 byte, maka kompilator akan menambahkan padding sebesar 3 byte setelah anggota char agar anggota integer dimulai pada alamat yang merupakan kelipatan dari 4. Padding ini berfungsi untuk menghindari terjadinya ketidaksesuaian dalam pengalamatan memori yang dapat menghambat kecepatan akses data.

Data Packing dalam Struktur

Sebaliknya, data packing adalah upaya untuk mengurangi ruang kosong yang dihasilkan oleh padding. Dalam beberapa situasi, pengembang mungkin ingin mengoptimalkan penggunaan memori dengan cara mengurangi atau menghilangkan padding yang ditambahkan secara otomatis oleh kompilator. Dengan data packing, struktur dapat diatur sedemikian rupa sehingga elemen-elemen dalam struktur ditempatkan dalam memori tanpa ada ruang kosong.

Meskipun teknik ini dapat mengurangi penggunaan memori, data packing sering kali dilakukan dengan pertimbangan bahwa hal itu dapat mempengaruhi kinerja akses memori. Dalam beberapa kasus, akses ke data yang tidak ter-align dengan benar dapat menyebabkan penurunan kinerja yang signifikan, terutama pada arsitektur komputer yang sangat mementingkan alignment, seperti pada sistem berbasis RISC. Oleh karena itu, data packing sering kali digunakan ketika penghematan memori lebih diutamakan daripada efisiensi dalam akses memori.

Namun, perlu dicatat bahwa penggunaan data packing dapat menyebabkan masalah lain, seperti kesalahan dalam kompatibilitas antar platform. Arsitektur komputer yang berbeda mungkin memiliki persyaratan alignment yang berbeda, sehingga penggunaan packing yang ekstrem dapat menyebabkan masalah ketika kode dijalankan pada sistem yang berbeda. Oleh karena itu, penggunaan data packing harus dilakukan dengan hati-hati, dan biasanya digunakan dalam situasi dimana memori menjadi sangat terbatas atau diperlukan untuk aplikasi yang memiliki kebutuhan khusus.

Pengaruh Padding dan Packing terhadap Kinerja

Konsep padding dan data packing tidak hanya berpengaruh pada penggunaan memori, tetapi juga dapat memengaruhi kinerja aplikasi secara keseluruhan. Padding yang dilakukan oleh kompilator untuk memastikan alignment anggota struktur pada alamat memori yang benar bertujuan untuk meningkatkan kinerja dalam hal kecepatan akses data. Pada arsitektur yang mendukung pengaksesan data dengan ukuran tertentu (misalnya, 4 byte untuk integer atau 8 byte untuk double), penggunaan padding memastikan bahwa data dapat diakses lebih cepat, karena CPU dapat mengambil data dalam unit yang lebih besar.

Sebaliknya, data packing yang mengurangi ruang kosong dapat menyebabkan data terletak pada alamat memori yang tidak sesuai dengan alignment yang optimal, sehingga bisa terjadi penurunan kinerja. Ini terjadi karena CPU mungkin memerlukan beberapa siklus lebih lama untuk mengakses data yang tidak ter-align dengan benar. Oleh karena itu, pengaruh padding dan packing terhadap kinerja harus diperhatikan dengan cermat, tergantung pada tujuan aplikasi dan lingkungan pengembangan yang digunakan.

Alasan dan Keuntungan Penggunaan Padding dan Packing

Padding dan packing diterapkan untuk berbagai alasan yang berkaitan dengan efisiensi dan kebutuhan aplikasi tertentu. Padding, meskipun menambah penggunaan memori, dapat membantu dalam memastikan kompatibilitas dan kinerja aplikasi dalam lingkungan yang berbeda. Pada arsitektur komputer yang sangat mengutamakan alignment, padding menjadi sangat penting untuk menjaga kinerja. Dengan memastikan bahwa setiap elemen struktur terletak pada alamat yang benar, memori dapat diakses lebih efisien, yang mengarah pada peningkatan kinerja aplikasi.

Sementara itu, data packing memberikan keuntungan dalam menghemat ruang memori, yang sangat berguna dalam aplikasi dengan batasan memori yang ketat, seperti aplikasi embedded atau aplikasi yang berjalan di perangkat dengan sumber daya terbatas. Meskipun packing bisa menurunkan kinerja akses memori, dalam beberapa kasus, keuntungan penghematan memori jauh lebih penting.

Kesimpulan

Pengelolaan memori dalam bahasa C, khususnya terkait dengan struktur data, padding, dan data packing, memainkan peran yang sangat penting dalam merancang aplikasi yang efisien. Meskipun konsep padding dan packing mungkin tampak seperti detail teknis yang rumit, pemahaman yang mendalam tentang keduanya dapat membantu pengembang untuk mengoptimalkan program sesuai dengan kebutuhan spesifik aplikasi. Padding memastikan bahwa data disusun dengan benar dalam memori, yang meningkatkan efisiensi akses, sementara data packing membantu menghemat ruang memori, meskipun kadang-kadang dapat mempengaruhi kinerja. Dalam praktiknya, keputusan untuk menggunakan padding atau packing harus didasarkan pada keseimbangan antara efisiensi memori dan kinerja aplikasi, serta pertimbangan kompatibilitas antar sistem.


Artikel ini akan dibaca oleh: Cuwi Nurti Ningrum, David Aldi Ramadhani, Diska Choirunnisa, Dwi Setyawan, dan Erlinda Suastika Dewi.

16 komentar untuk "Anggota Structure, Padding, dan Data Packing Bahasa C"

  1. Apakah alignment diterapkan untuk stack pada Bahasa C?

    BalasHapus
    Balasan
    1. Ya, karena stack juga merupakan memori. Pemrogram sistem harus memuat pointer stack dengan alamat memori yang disejajarkan dengan benar. Umumnya, prosesor tidak akan memeriksa penjajaran stack, yang merupakan tanggung jawab pemrogram untuk memastikan penyelarasan yang tepat dari memori stack. Setiap misalignment akan menyebabkan kejutan waktu berjalan. Misalnya, jika panjang kata prosesor adalah 32 bit, maka pointer stack juga harus disejajarkan menjadi kelipatan 4 byte.

      Hapus
  2. Bagaimana prosesor menangani tipe char Bahasa C?

    BalasHapus
    Balasan
    1. Jika data char ditempatkan di penampung lain, seperti penampung 0, maka hal tersebut akan membuah nilai ditempatkan pada jalur data yang salah selama pembacaan memori. Biasanya prosesor akan mengenali tipe data berdasarkan instruksi seperti LDRB pada prosesor ARM. Bergantung pada tempat penyimpanannya, dimana prosesor akan menggeser byte ke jalur data yang paling tidak signifikan.

      Hapus
  3. Ketika argumen diteruskan pada stack, apakah stack tersebut akan tunduk pada alignment Bahasa C?

    BalasHapus
    Balasan
    1. Ya. Kompilator akan membantu programmer dalam membuat keselarasan yang tepat. Misalnya, jika nilai 16-bit didorong ke stack selebar 32-bit, dimana nilainya secara otomatis diisi dengan nol hingga 32 bit. Perhatikan program berikut.

      Hapus
    2. void argument_alignment_check( char c1, char c2 )

      {

      // Downward stack

      // (on upward stack the output will be negative)

      printf("Displacement %d\n", (int)&c2 - (int)&c1);

      }

      Hapus
    3. Outputnya akan menjadi 4 pada prosesor tipe 32 bit. Hal tersebut terjadi karena setiap karakter akan menempati 4 byte karena persyaratan penyelarasan.

      Hapus
  4. Apa yang akan terjadi jika mencoba untuk mengakses sebuah misaligned data pada Bahasa C?

    BalasHapus
    Balasan
    1. Hal tersebut bergantung pada arsitektur prosesor. Jika akses tidak selaras, maka prosesor secara otomatis mengeluarkan siklus baca memori yang cukup dan mengemas data dengan benar ke dalam bus data. Hukumannya ada pada kinerja. Di mana beberapa prosesor tidak akan memiliki dua baris alamat terakhir, yang berarti tidak ada cara untuk mengakses batas byte ganjil, dimana setiap akses data harus selaras (4 byte) dengan benar. Akses yang tidak selaras adalah pengecualian penting pada prosesor tersebut. Jika pengecualian diabaikan, maka data yang dibaca akan salah dan menyebabkan error.

      Hapus
  5. Apakah ada cara untuk menanyakan persyaratan penyelarasan atau alignment dari tipe data pada Bahasa C?

    BalasHapus
    Balasan
    1. Ya. Kompilator menyediakan ekstensi non standar untuk kebutuhan tersebut. Misalnya, __alignof() di Visual Studio yang dapat membantu mendapatkan persyaratan penyelarasan tipe data.

      Hapus
  6. Ketika pembacaan memori efisien dalam membaca 4 byte sekaligus pada mesin 32 bit, mengapa tipe ganda harus disejajarkan pada batas 8 byte pada Bahasa C?

    BalasHapus
    Balasan
    1. Penting untuk dicatat bahwa sebagian besar prosesor akan memiliki co-processor matematis, yang disebut Floating Point Unit (FPU). Setiap operasi floating point dalam kode akan diterjemahkan ke dalam instruksi FPU. Prosesor utama tidak ada hubungannya dengan eksekusi floating point. Semua ini akan dilakukan di belakang layar.

      Hapus
    2. Sesuai standar, tipe ganda akan menempati 8 byte. Dan, setiap operasi floating point yang dilakukan di FPU akan memiliki panjang sebesar 64 bit, bahkan tipe float akan dipromosikan ke 64 bit sebelum dieksekusi.

      Hapus
    3. Panjang 64 bit register FPU memaksa tipe ganda atau double untuk dialokasikan pada batas 8 byte. Misal diasumkan tidak memiliki informasi konkret dalam hal operasi FPU, maka pengambilan data yang dihasilkan mungkin berbeda, karena masuk ke FPU. Oleh karena itu, decoding alamat akan berbeda untuk tipe double yang diharapkan berada pada batas 8 byte. Artinya, sirkuit decoding alamat unit floating point tidak akan memiliki 3 pin terakhir.

      Hapus

Hubungi admin melalui Wa : +62-896-2414-6106

Respon komentar 7 x 24 jam, mohon bersabar jika komentar tidak langsung dipublikasi atau mendapatkan balasan secara langsung.

Bantu admin meningkatkan kualitas blog dengan melaporkan berbagai permasalahan seperti typo, link bermasalah, dan lain sebagainya melalui kolom komentar.

- Ikatlah Ilmu dengan Memostingkannya -