Belajar C Language - Advanced Memory Techniques
Episode 17 of 24

Belajar C Language - Advanced Memory Techniques

Episode ini memperdalam pengelolaan memori: memory alignment, padding, dan pointer arithmetic, custom allocators dan memory pool dasar untuk alokasi cepat, memory sanitizer dan leak detection, serta cara menulis reliable low-level code untuk embedded dan real-time.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Alokasi memori memakai malloc cukup untuk hampir semua kasus, tetapi sistem yang sangat menuntut kecepatan dan determinisme membutuhkan pemahaman yang lebih dalam. Episode 17 membahas lapisan lanjutan: bagaimana data disejajarkan di memori, bagaimana membangun allocator khusus, dan bagaimana menulis kode yang andal untuk embedded dan real-time.

Memory alignment menentukan bagaimana struct tersusun dalam memori dan berapa banyak ruang terbuang sebagai padding. Custom allocator memungkinkan alokasi yang cepat dan deterministik untuk aplikasi yang tidak boleh macet. Sanitizers dan leak detection memastikan kebenaran di semua level.

Ini episode paling dekat dengan perangkat keras dalam series ini, dan keterampilannya langsung dipakai di episode 18 dan 21.

Memory Alignment, Padding, dan Pointer Arithmetic

Mengapa Aligment Penting

CPU membaca memori dalam potongan dengan ukuran tertentu. Jika sebuah int diletakkan di alamat yang tidak kelipatan 4, aksesnya lebih lambat atau bahkan error di beberapa arsitektur. Compiler menyelaraskan otomatis, tetapi menambahkan padding di antara anggota struct:

Padding struct
#include <stdio.h>
#include <stddef.h>
 
struct Contoh {
    char a;
    int b;
    char c;
};
 
int main(void) {
    printf("ukuran: %zu\n", sizeof(struct Contoh));
    printf("offset b: %zu\n", offsetof(struct Contoh, b));
    return 0;
}

Meski char berukuran 1 dan int berukuran 4, sizeof(struct Contoh) bisa menghasilkan 12 karena padding menyelaraskan b ke offset kelipatan 4. Urutan anggota yang mengelompokkan tipe besar lebih dulu mengurangi padding, seperti dibahas di episode 15.

Pointer Arithmetic dan Alignment

Aritmatika pointer memajukan alamat sesuai ukuran tipe. Menambah pointer int * sebesar 1 memajukan alamat 4 byte. Untuk memindahkan blok memori mentah, gunakan pointer ke unsigned char yang langkahnya 1 byte, atau fungsi memcpy yang menangani overlap. Pahami bahwa menerapkan aritmatika pointer pada tipe yang salah menghasilkan perilaku tak terdefinisi.

Custom Allocators dan Memory Pool

Mengapa Membangun Allocator Sendiri

malloc serbaguna tetapi membawa overhead pencarian dan tidak deterministik — lama eksekusinya bervariasi. Sistem real-time dan game sering memakai allocator khusus yang dialokasikan sekali di awal, lalu dibagikan kembali dengan cepat.

Memory Pool Dasar

Memory pool menyimpan blok-blok berukuran tetap yang dipakai ulang:

Memory pool sederhana
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
#define N_BLOK 64
#define UKURAN 256
 
static unsigned char arena[N_BLOK][UKURAN];
static int dipakai[N_BLOK];
 
void *pool_alloc(void) {
    for (int i = 0; i < N_BLOK; i++) {
        if (!dipakai[i]) {
            dipakai[i] = 1;
            return arena[i];
        }
    }
    return NULL;
}
 
void pool_free(void *p) {
    for (int i = 0; i < N_BLOK; i++) {
        if (arena[i] == p) {
            dipakai[i] = 0;
            return;
        }
    }
}

Fungsi pool_alloc() mencari blok yang tersedia secara linear — deterministik dan tanpa pencarian sistem. pool_free memetakan pointer kembali ke blok dengan perbandingan alamat. Arena dialokasikan statis, jadi tidak ada overhead heap saat runtime.

Batasan Memory Pool

Pool berukuran tetap membatasi jumlah blok dan ukuran maksimalnya. Alokasi yang melebihi kapasitas mengembalikan NULL, jadi pemakai wajib merancang dengan batas itu sejak awal. Inilah trade-off yang umum di embedded: determinisme dibeli dengan fleksibilitas yang dikorbankan.

Memory Sanitizer dan Leak Detection

Lapisan Pemeriksaan Memori

Kombinasi sanitizer menemukan hampir semua kesalahan memori saat pengembangan:

Pemeriksaan memori menyeluruh
gcc -g -fsanitize=address,undefined -fno-omit-frame-pointer program.c -o program
valgrind --leak-check=full --error-exitcode=1 ./program

Bendera -fno-omit-frame-pointer memastikan stack trace tetap lengkap di laporan sanitizer. valgrind --leak-check=full menemukan blok yang bocor dan memeriksa pemakaian setelah bebas. Jalankan keduanya di setiap tahap pengembangan — kesalahan yang tertangkap lebih awal jauh lebih murah.

Diagnosa Laporan

Laporan AddressSanitizer menunjukkan instruksi yang salah dan stack trace panggilan, sementara Valgrind melaporkan di baris mana blok dialokasikan dan bocor. Biasakan membaca keduanya dengan teliti; tumpukan panggilan yang tercetak hampir selalu menunjuk langsung ke akar masalah.

Reliable Low-Level Code untuk Embedded dan Real-Time

Prinsip Keandalan

Kode untuk embedded dan real-time menuntut ketegasan: tidak ada alokasi heap di jalur kritis, tidak ada printf di loop yang sensitif waktu, dan tidak ada perilaku yang bergantung pada optimasi compiler yang tidak terdokumentasi. Semua jalur kode harus deterministik.

Disiplin yang Diterapkan

Dua praktik utama: fungsi bertanggung jawab penuh atas memori yang dialokasikannya, dan setiap akses memori diperiksa batasnya. Kompilasi dengan peringatan paling ketat:

Build ketat untuk low-level
gcc -std=c11 -Wall -Wextra -Werror -pedantic program.c -o program

Kombinasi -std=c11 -Wall -Wextra -Werror -pedantic memaksa kode mematuhi standar dan memperlakukan semua peringatan sebagai error. Ini adalah standar yang dipakai firmware di industri otomotif dan medis — di mana bug bukan sekadar biaya, tetapi ancaman keselamatan.

Tip

Di embedded, alat seperti static analyzer (episode 11) dan MISRA C membantu menjaga disiplin. Kode yang bisa dibuktikan benar lebih bernilai daripada kode yang cepat tetapi ragu-ragu.

Penutup

Inti yang harus dibawa pulang:

  • Alignment membuat CPU membaca data efisien, padding menambah ukuran struct.
  • Aritmatika pointer memajukan alamat sesuai ukuran tipe.
  • Memory pool memberi alokasi cepat dan deterministik dengan batas tetap.
  • AddressSanitizer, UBSan, dan Valgrind menemukan hampir semua bug memori.
  • Jalankan sanitizers dan leak check di setiap tahap pengembangan.
  • Kode real-time harus deterministik: no heap di jalur kritis dan batas selalu diperiksa.

Di episode 18 selanjutnya kita akan membahas system programming dan OS interaction — system call dasar dan wrapper, process creation dengan fork, exec, dan wait, signals, pipes, dan inter-process communication, serta file descriptors, select dan poll untuk event-driven I/O.

Belajar C Language - Advanced Memory Techniques | Belajar C Language