Bekerja dengan array di C memberi kita kekuatan sekaligus tanggung jawab. Karena C tidak melakukan bounds checking otomatis dan tidak menyimpan panjang array sebagai atribut, banyak kesalahan halus bisa lolos kompilasi tetapi berakibat undefined behavior saat runtime. Pada chapter ini kita akan menelusuri empat kesalahan paling umum: akses out-of-bounds, penggunaan array tak terinisialisasi, sizeof pada parameter fungsi yang sudah meluruh, serta off-by-one error pada loop. Memahami kesalahan-kesalahan ini akan membantu kita menulis kode yang lebih aman.
Kesalahan 1: Akses Out-of-Bounds #
Akses arr[n] pada array berukuran n adalah undefined behavior—indeks valid adalah 0 sampai n-1. Compiler tidak akan otomatis memperingatkan, dan program bisa membaca nilai sampah, crash, atau bahkan terlihat “berjalan normal” sementara data korupsi terjadi diam-diam.
#include <stdio.h>
int main(void) {
/* 1. Out-of-bounds: UNDEFINED BEHAVIOR */
int arr[5] = {10, 20, 30, 40, 50};
printf("arr[5] (out-of-bounds) = %d (UB, bisa nilai apa saja)\n", arr[5]);
printf("arr[10] (jauh OOB) = %d (UB)\n", arr[10]);
/* 2. Array tak terinisialisasi: berisi sampah memori */
int sampah[4];
printf("\nArray tak terinisialisasi:\n");
for (int i = 0; i < 4; i++) {
printf(" sampah[%d] = %d\n", i, sampah[i]);
}
/* 3. sizeof pada parameter function: TIDAK mengembalikan ukuran array asli */
/* (diperlihatkan di chapter array-sebagai-parameter) */
/* 4. Loop off-by-one: i <= n seharusnya i < n */
int total = 0;
int n = 5;
for (int i = 0; i < n; i++) { /* benar */
total += arr[i];
}
printf("\nTotal (loop benar i<n): %d\n", total);
/* 5. Konsekuensi membandingkan signed/unsigned */
int arr_s[3] = {1, 2, 3};
/* Bahaya: jika pakai size_t i dan array kosong, loop underflow */
size_t panjang = sizeof(arr_s) / sizeof(arr_s[0]);
printf("Panjang arr_s = %zu\n", panjang);
return 0;
}Output yang Dihasilkan:
arr[5] (out-of-bounds) = 0 (UB, bisa nilai apa saja)
arr[10] (jauh OOB) = 1991923024 (UB)
Array tak terinisialisasi:
sampah[0] = 0
sampah[1] = 0
sampah[2] = 0
sampah[3] = 0
Total (loop benar i<n): 150
Panjang arr_s = 3Mari kita telaah baris-baris kode di atas:
- Baris 6 (
int arr[5] = {10, 20, 30, 40, 50};): array berukuran 5 elemen. Indeks valid:0..4. - Baris 8 (
printf("arr[5] (out-of-bounds) = %d ...", arr[5]);): aksesarr[5]di luar batas. Output menunjukkan0di sandbox ini, tetapi ini tidak terprediksi—bisa berupa angka acak, dan program bisa crash pada eksekusi lain. - Baris 9 (
printf("arr[10] (jauh OOB) ...", arr[10]);): aksesarr[10]jauh di luar batas. Output1991923024adalah nilai sampah dari memori di luar array. Ini jelas menunjukkan data tak terduga. - Baris 12 (
int sampah[4];): deklarasi tanpa inisialisasi. Compiler memberi peringatan-Wmaybe-uninitialized. Pada sandbox ini nilai kebetulan0karena OS menginisialisasi ulang halaman memori—tetapi perilaku ini tidak dijamin. - Baris 19-22 (
for (int i = 0; i < n; i++) total += arr[i];): loop benar. Kondisii < n(bukani <= n) memastikan indeks tidak melebihin - 1= 4. - Baris 27-28 (
size_t panjang = sizeof(arr_s) / sizeof(arr_s[0]);): idiom yang benar untuk menghitung panjang array lokal. Hasilnya3sesuai jumlah elemen.
Kesalahan 2: sizeof pada Parameter Fungsi #
Kesalahan klasik: menulis fungsi yang menerima array sebagai parameter lalu memakai sizeof(arr) / sizeof(arr[0]) di dalam fungsi untuk “menghitung panjang”. Karena array meluruh menjadi pointer, hasilnya adalah sizeof(int*) / sizeof(int) = 8/4 = 2 pada 64-bit—bukan panjang sebenarnya.
#include <stdio.h>
/* Demonstrasi: sizeof pada parameter array TIDAK mengembalikan
ukuran array asli karena decay menjadi pointer. */
int jumlah_elemen_salah(int arr[]) {
/* Akan mengembalikan ukuran pointer, bukan ukuran array */
return (int)(sizeof(arr) / sizeof(arr[0]));
}
int jumlah_elemen_benar(int arr[], int n) {
/* Pemanggil harus menyertakan panjang secara eksplisit */
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
int main(void) {
int data[5] = {10, 20, 30, 40, 50};
/* Versi salah: sizeof pada parameter */
int len = jumlah_elemen_salah(data);
printf("Hasil sizeof(arr)/sizeof(arr[0]) pada parameter: %d\n", len);
printf("(Seharusnya 5, tapi yang dihitung adalah sizeof(int*)/sizeof(int) = 8/4 = 2)\n");
/* Versi benar: lewatkan n secara eksplisit */
int total = jumlah_elemen_benar(data, 5);
printf("Total yang benar: %d\n", total);
return 0;
}Output yang Dihasilkan:
Hasil sizeof(arr)/sizeof(arr[0]) pada parameter: 2
(Seharusnya 5, tapi yang dihitung adalah sizeof(int*)/sizeof(int) = 8/4 = 2)
Total yang benar: 150Pada contoh di atas, fungsi jumlah_elemen_salah mengembalikan 2 padahal array asli berukuran 5. Compiler memberi peringatan -Wsizeof-array-argument untuk mengingatkan bahwa sizeof pada parameter array mengembalikan ukuran pointer. Solusi yang benar: lewatkan panjang array sebagai parameter terpisah, seperti pada fungsi jumlah_elemen_benar(int arr[], int n).
Ringkasan Kesalahan Umum #
| Kesalahan | Akibat | Pencegahan |
|---|---|---|
Akses arr[n] pada array berukuran n | undefined behavior, nilai sampah atau crash | Pakai i < n, bukan i <= n |
| Array tak terinisialisasi lalu dibaca | undefined behavior, nilai acak | Inisialisasi dengan {0} atau memset |
sizeof(arr) pada parameter fungsi | Mengembalikan ukuran pointer, bukan array | Teruskan panjang sebagai parameter |
Loop for (i = 0; i <= n; i++) | off-by-one, akses arr[n] | Pakai i < n |
| Modifikasi string literal | Segmentation fault | Pakai const char * atau array char[] lokal |