Setiap programmer C, sejak hari pertama menulis printf("Halo\n"), akan berhadapan dengan kesalahan. Beberapa kesalahan ditolak langsung oleh compiler sebelum program sempat berjalan, beberapa baru terlihat saat program dijalankan, dan beberapa lainnya diam-diam menghasilkan angka yang salah tanpa pesan apa pun. Pada chapter ini kita akan mengelompokkan kesalahan menjadi tiga kategori utama—compile-time error, runtime error, dan logic error—lalu menambahkan kategori keempat yaitu system error yang muncul dari interaksi program dengan dunia luar seperti file system dan network. Pengelompokan ini bukan akademis belaka: setiap kategori butuh strategi penanganan berbeda, dan salah mengenali kategori akan membuat kita menulis error handling di tempat yang salah.
Tiga Kategori Utama: Compile-time, Runtime, Logic #
Compile-time error adalah kesalahan yang dideteksi oleh compiler saat membaca source code. Kategori ini terdiri dari syntax error (titik koma hilang, kurung tidak seimbang) dan type error (mengoper int* ke fungsi yang minta char*). Compiler menolak membangun binary, sehingga program tidak pernah dijalankan. Kesalahan ini paling mudah diperbaiki karena compiler menyebutkan file, line number, dan pesan yang cukup deskriptif.
Runtime error adalah kesalahan yang baru terjadi saat program dijalankan. Contohnya adalah pembagian oleh nol, null pointer dereference, array out of bounds, dan kehabisan memori. Program ter-compile dengan baik, tetapi crash atau menghasilkan undefined behavior di tengah eksekusi. Kategori ini butuh pengecekan eksplisit di kode—compiler tidak bisa menebak bahwa b akan bernilai nol.
Logic error adalah kategori paling sulit dideteksi. Program ter-compile bersih, berjalan tanpa crash, tetapi menghasilkan output yang salah. Contoh klasik: rata-rata dihitung dengan membagi jumlah oleh n-1 bukan n, atau loop berhenti satu iterasi terlalu cepat. Tidak ada pesan kesalahan apa pun; kita hanya menyadari sesuatu salah ketika hasil perhitungan tidak masuk akal.
Demonstrasi Logic Error #
Mari kita lihat logic error secara konkret. Program berikut menghitung rata-rata sebuah array. Kode ter-compile bersih, berjalan tanpa crash, tetapi menghasilkan angka yang salah.
#include <stdio.h>
/* Contoh logic error: rata-rata dihitung dari n-1 elemen, bukan n.
Program berjalan tanpa crash tetapi hasilnya salah. */
int main(void) {
int data[] = {10, 20, 30, 40, 50};
int n = 5;
int jumlah = 0;
for (int i = 0; i < n; i++) {
jumlah += data[i];
}
/* BUG: pembagi seharusnya n, bukan n-1 */
double rata = (double)jumlah / (n - 1);
printf("Jumlah = %d\n", jumlah);
printf("n = %d\n", n);
printf("Rata = %.2f <-- seharusnya 30.00\n", rata);
return 0;
}Output yang Dihasilkan:
Jumlah = 150
n = 5
Rata = 37.50 <-- seharusnya 30.00Rata-rata seharusnya 150/5 = 30.00, tetapi program mencetak 37.50 karena pembagi adalah n - 1 alias 4. Compiler tidak bisa mengetahui bahwa n - 1 salah secara semantik; bagi compiler, ekspresi itu legal. Inilah ciri khas logic error: tidak ada pesan kesalahan, hanya hasil yang tidak masuk akal.
Mari kita telaah baris-baris kode di atas:
- Baris 7 (
int data[] = {10, 20, 30, 40, 50};): menginisialisasi array 5 elemen dengan nilai yang jumlahnya 150. Rata-rata yang benar adalah 30. - Baris 11-13 (
for (...) jumlah += data[i];): menjumlahkan semua elemen ke dalamjumlah. Loop ini benar—menggunakan<sebagai kondisi agar tidak out of bounds. - Baris 16 (
double rata = (double)jumlah / (n - 1);): di sinilah bug bersembunyi. Pembagin - 1menghasilkan 4, padahal seharusnyanyaitu 5. Cast(double)hanya memengaruhi tipe hasil, bukan memperbaiki bug. - Baris 18-20 (
printf(...)): mencetak hasil. Output 37.50 tampak “wajar” karena mendekati 30, sehingga bug semacam ini sering lolos dari pemeriksaan manual.
Demonstrasi Runtime Error #
Runtime error bisa membuat program crash. Contoh paling sering adalah pembagian oleh nol. Tanpa pengecekan, instruksi a / b ketika b == 0 memicu floating-point exception (sinyal SIGFPE) dan kernel menghentikan proses. Strategi penanganan yang benar adalah memeriksa kondisi bahaya sebelum instruksi rentan dieksekusi.
#include <stdio.h>
/* Contoh runtime error yang ditangani: pembagian oleh nol.
Tanpa pengecekan, program akan crash (floating-point exception). */
int main(void) {
int a = 100;
int b = 0;
if (b == 0) {
fprintf(stderr, "Runtime error: pembagi nol terdeteksi sebelum dieksekusi\n");
return 1;
}
int hasil = a / b;
printf("Hasil = %d\n", hasil);
return 0;
}Output yang Dihasilkan:
Runtime error: pembagi nol terdeteksi sebelum dieksekusiProgram keluar dengan exit code 1 (bukan 0). Pesan kesalahan ditulis ke stderr (lewat fprintf(stderr, ...)) agar tetap terlihat meski output stream diarahkan ke file. Strategi if (b == 0) return 1; sebelum pembagian inilah inti dari defensive programming yang akan kita pelajari di chapter terpisah.
Demonstrasi System Error #
System error adalah kategori tambahan: kesalahan yang bukan muncul dari logika program sendiri, melainkan dari interaksi dengan sistem operasi atau dunia luar. Contoh paling sering adalah file system: membuka file yang tidak ada, menulis ke path tanpa izin, atau socket yang terputus. Fungsi standar seperti fopen mengembalikan NULL dan menetapkan errno (dibahas di chapter khusus).
#include <stdio.h>
/* Contoh system error: mencoba membuka file yang tidak ada.
fopen mengembalikan NULL dan program menangani kondisi tersebut. */
int main(void) {
const char *path = "/tmp/file_tidak_ada_demo.txt";
FILE *fp = fopen(path, "r");
if (fp == NULL) {
fprintf(stderr, "System error: tidak dapat membuka '%s'\n", path);
return 1;
}
char baris[256];
if (fgets(baris, sizeof(baris), fp) != NULL) {
printf("Isi: %s", baris);
}
fclose(fp);
return 0;
}Output yang Dihasilkan:
System error: tidak dapat membuka '/tmp/file_tidak_ada_demo.txt'Program tidak crash; ia memberi pesan yang jelas dan keluar dengan exit code 1. Inilah perbedaan mendasar dengan runtime error: system error dapat diprediksi dan ditangani, karena fungsi sistem seperti fopen sudah dirancang memberi sinyal kegagalan (melalui NULL dan errno). Tugas kita hanyalah memeriksa sinyal itu.
Tabel Ringkas Strategi Penanganan #
| Kategori | Kapan Terdeteksi | Cara Penanganan Utama |
|---|---|---|
| Compile-time | Saat gcc dijalankan | Perbaiki sintaks/tipe; tidak perlu kode runtime |
| Runtime | Saat program berjalan | Pengecekan eksplisit (if (b == 0), if (ptr == NULL)) |
| Logic | Saat output diperiksa | Unit test, assertion, review manual |
| System | Saat pemanggilan API gagal | Cek return value + errno + perror |