Header <assert.h> menyediakan makro assert untuk menegakkan invariant saat program berjalan dan static_assert (sejak C11) untuk menegakkan invariant saat kompilasi. assert adalah alat debugging: bila kondisi yang dinyatakan bernilai false, program dihentikan dengan pesan yang menunjukkan file, baris, dan ekspresi yang gagal. Dengan assert, kita dapat mendeteksi bug lebih awal—sebelum data corruption menyebar. static_assert lebih kuat lagi: gagal kompilasi bila kondisi konstanta false, sehingga bug terdeteksi sebelum program bisa dijalankan. Pada chapter ini kita akan meninjau penggunaan kedua makro ini beserta praktik amannya.

assert: Invariant Saat Runtime #

Makro assert(expr) mengevaluasi expr saat program berjalan. Bila expr bernilai false (nol), assert memanggil abort() dan mencetak pesan ke stderr yang berisi nama file, nomor baris, dan ekspresi yang gagal. Bila expr bernilai true, assert tidak melakukan apa-apa. Pola umum: tempatkan assert di awal fungsi untuk memvalidasi precondition—argumen yang harus benar agar fungsi bekerja.

c
#include <stdio.h>
#include <assert.h>

/* Fungsi dengan preconditions */
static double akar_kuadrat(double x) {
    /* assert: jika kondisi false, program dihentikan */
    assert(x >= 0.0 && "akar_kuadrat: input tidak boleh negatif");
    return x;   /* (di program nyata: hitung akar) */
}

int main(void) {
    printf("akar_kuadrat(16.0) = %.2f\n", akar_kuadrat(16.0));
    printf("akar_kuadrat(0.0)  = %.2f\n", akar_kuadrat(0.0));

    /* Kasus ini akan memicu assertion failure */
    printf("Mencoba akar_kuadrat(-1.0)...\n");
    double r = akar_kuadrat(-1.0);
    printf("Hasil: %.2f (tidak akan tercapai)\n", r);

    return 0;
}

Output yang Dihasilkan (stdout):

text
akar_kuadrat(16.0) = 16.00
akar_kuadrat(0.0)  = 0.00
Mencoba akar_kuadrat(-1.0)...

Output yang Dihasilkan (stderr):

text
test_assert1: /tmp/test_assert1.c:7: akar_kuadrat: Assertion `x >= 0.0 && "akar_kuadrat: input tidak boleh negatif"' failed.
Aborted

Mari kita telaah baris-baris kode di atas:

  1. Baris 6 (assert(x >= 0.0 && "akar_kuadrat: input tidak boleh negatif");): ekspresi gabungan. Bagian x >= 0.0 adalah kondisi yang diperiksa. Bagian "akar_kuadrat: input tidak boleh negatif" adalah string literal—string selalu bernilai true (non-NULL pointer), jadi tidak memengaruhi kondisi logika, tetapi ikut tercetak dalam pesan error.
  2. Baris 13 (akar_kuadrat(16.0)): pemanggilan dengan input valid. assert mengevaluasi kondisi (true), tidak melakukan apa-apa, dan fungsi mengembalikan 16.0.
  3. Baris 16 (akar_kuadrat(-1.0)): pemanggilan dengan input invalid. assert mendapati kondisi false, mencetak pesan ke stderr (nama file, baris, nama fungsi, ekspresi yang gagal), lalu memanggil abort(). Program dihentikan dengan exit code 134 (128 + 6 = SIGABRT).
  4. Baris 17 (printf("Hasil: ...")): tidak pernah dieksekusi karena program sudah dihentikan di baris 16.

static_assert: Invariant Saat Kompilasi (C11) #

Makro static_assert(expr, msg) diperkenalkan di C11 (sebagai keyword _Static_assert yang di-alias oleh <assert.h>). Ekspresi expr harus berupa integer constant expression yang dapat dievaluasi saat kompilasi. Bila expr bernilai false, kompilasi gagal dengan pesan msg. Ini berguna untuk menegakkan constraint platform—misalnya kode yang hanya boleh jalan di 64-bit.

c
#include <stdio.h>
#include <assert.h>

/* static_assert (C11): dipakai pada compile-time */
static_assert(sizeof(int) >= 4, "int harus minimal 4 byte");
static_assert(sizeof(void*) == 8, "Kode ini hanya untuk platform 64-bit");

/* Cek konstanta pada saat kompilasi */
#define MAX_BUFFER 256
static_assert(MAX_BUFFER % 16 == 0, "MAX_BUFFER harus kelipatan 16");

int main(void) {
    int arr[MAX_BUFFER];
    arr[0] = 42;
    printf("Kompilasi sukses: sizeof(int)=%zu, MAX_BUFFER=%d\n",
           sizeof(int), MAX_BUFFER);
    (void)arr;
    return 0;
}

Output yang Dihasilkan:

text
Kompilasi sukses: sizeof(int)=4, MAX_BUFFER=256

Tiga static_assert di awal program memverifikasi tiga kondisi: (1) sizeof(int) >= 4—kompilasi gagal bila platform memiliki int 16-bit; (2) sizeof(void*) == 8—kompilasi gagal bila platform 32-bit; (3) MAX_BUFFER % 16 == 0—kompilasi gagal bila MAX_BUFFER bukan kelipatan 16. Semua kondisi bernilai true pada platform x86_64 Linux, jadi kompilasi berhasil dan program berjalan normal. Bila salah satu kondisi false, kompilasi gagal dengan pesan seperti error: static assertion failed: "int harus minimal 4 byte".

Mematikan assert dengan NDEBUG #

Makro assert dapat dimatikan dengan mendefinisikan makro NDEBUG sebelum #include <assert.h>. Dengan NDEBUG terdefinisi, assert di-expand menjadi no-op (kosong)—tidak ada pemeriksaan, tidak ada overhead. Pola ini dipakai untuk build production di mana kecepatan lebih penting daripada pengecekan runtime.

c
#include <stdio.h>
#include <assert.h>

/* assert bisa dimatikan dengan macro NDEBUG */
/* Compile dengan -DNDEBUG untuk mematikan assertion */

static int cari_elemen(const int *arr, size_t n, int target) {
    /* Precondition: array tidak boleh NULL */
    assert(arr != NULL && "array tidak boleh NULL");
    assert(n > 0 && "ukuran array harus > 0");

    for (size_t i = 0; i < n; i++) {
        if (arr[i] == target) return (int)i;
    }
    return -1;
}

int main(void) {
    int data[] = {10, 20, 30, 40, 50};
    size_t n = sizeof(data) / sizeof(data[0]);

    printf("cari 30 di indeks: %d\n", cari_elemen(data, n, 30));
    printf("cari 99 di indeks: %d\n", cari_elemen(data, n, 99));

    /* Tanpa NDEBUG, pemanggilan ini akan menghentikan program */
    /* Dengan -DNDEBUG, pemanggilan ini dereferensi NULL -> crash */
    /* printf("dengan NULL: %d\n", cari_elemen(NULL, 5, 1)); */

    return 0;
}

Output yang Dihasilkan (kompilasi tanpa dan dengan -DNDEBUG sama):

text
cari 30 di indeks: 2
cari 99 di indeks: -1

Perintah kompilasi gcc -std=c11 -DNDEBUG -o program program.c menghasilkan binary di mana semua assert menjadi no-op. Dalam mode ini, pemanggilan cari_elemen(NULL, 5, 1) tidak lagi menghentikan program dengan pesan—sebaliknya, loop mencoba mengakses arr[0] yang merupakan dereferensi pointer NULL, menyebabkan segmentation fault tanpa pesan yang berguna. Inilah alasan mengapa assert tidak boleh menjadi satu-satunya defense untuk input yang tidak dipercaya—input dari pengguna atau jaringan harus tetap divalidasi dengan if eksplisit.