Array menyimpan beberapa nilai bertipe sama dalam satu nama variabel yang berderet di memori. Topik ini membahas deklarasi array satu dimensi, pengindeksan, dan penelusuran dengan perulangan. Satu perbedaan penting dari banyak bahasa lain juga dibahas, yaitu C tidak memeriksa batas pada saat runtime. Setelah membacanya, array dapat dideklarasikan, diisi, dan diproses sambil mengetahui risiko yang menyertainya.
Deklarasi dan inisialisasi array#
Array dideklarasikan dengan menuliskan tipe elemen, nama array, lalu jumlah elemen di dalam tanda kurung siku. Deklarasi int nilai[5]; menyediakan lima sel memori bertipe int yang berurutan, dan ukurannya tetap sejak proses kompilasi. Pada bentuk deklarasi polos seperti itu, isinya belum ditentukan, sehingga setiap elemen perlu diberi nilai sebelum dibaca.
Nilai awal dapat dituliskan sekaligus menggunakan tanda kurung kurawal. Apabila hanya sebagian elemen yang diberi nilai, sisa elemennya otomatis berisi 0. Bila tanda kurung siku dibiarkan kosong, ukuran array mengikuti banyaknya nilai awal yang dituliskan. Ketiga bentuk tersebut dirangkum pada contoh berikut.
// tidak-dijalankan
int nilai[5]; /* lima int, isinya belum ditentukan */
int suhu[3] = {28, 30, 27}; /* deklarasi sekaligus inisialisasi */
int tekanan[4] = {1013}; /* sisanya otomatis 0: {1013, 0, 0, 0} */
int umur[] = {19, 21, 20}; /* ukuran mengikuti inisialisator: 3 */Ukuran array pada bentuk dasar wajib diketahui pada saat kompilasi, misalnya berupa bilangan atau konstanta yang sudah didefinisikan. Karena ukurannya tidak dapat berubah, array cocok untuk data yang jumlahnya diketahui di awal, seperti nilai lima ujian atau suhu tiga hari. Kebutuhan ukuran yang baru diketahui saat program berjalan ditangani memori dinamis pada modul tersendiri.
Pengindeksan dimulai dari nol#
Setiap sel pada array disebut elemen dan diakses melalui indeks yang dimulai dari 0. Array dengan lima elemen memiliki indeks 0 sampai 4, sehingga elemen terakhirnya adalah nilai[4] dan bukan nilai[5]. Membaca dan mengisi elemen memakai bentuk yang sama dengan variabel biasa, misalnya nilai[0] = 78; untuk pengisian dan x = nilai[0]; untuk pembacaan.
Diagram berikut memperlihatkan susunan lima elemen int di memori beserta offset byte setiap elemen terhadap awal array.
Tanda kurung siku pada deklarasi dan pada pengaksesan memang terlihat sama, tetapi perannya berbeda. Pada deklarasi, angka di dalamnya menyatakan kapasitas array. Pada pengaksesan, isinya adalah indeks elemen yang dituju, dan indeks tersebut boleh berupa variabel apa pun bertipe bilangan bulat.
Menelusuri array dengan perulangan#
Pengolahan array hampir selalu berpasangan dengan perulangan for karena indeksnya berurutan. Pola yang dipakai adalah memulai penghitung dari 0, berhenti sebelum mencapai jumlah elemen, lalu memakai penghitung tersebut sebagai indeks. Program berikut mencetak seluruh elemen, menjumlahkannya, lalu menghitung rata-ratanya.
#include <stdio.h>
int main(void) {
int nilai[] = {78, 85, 90, 70, 82};
int jumlah = 0;
for (int i = 0; i < 5; i++) {
printf("nilai[%d] = %d\n", i, nilai[i]);
jumlah += nilai[i];
}
printf("Jumlah = %d\n", jumlah);
printf("Rata-rata = %.1f\n", jumlah / 5.0);
return 0;
}nilai[0] = 78
nilai[1] = 85
nilai[2] = 90
nilai[3] = 70
nilai[4] = 82
Jumlah = 405
Rata-rata = 81.0Untuk mencoba program di atas, kompilasi berkasnya dengan peringatan aktif.
# kompilasi program contoh dengan peringatan aktif
gcc -Wall -Wextra nilai-rata-rata.c -o nilai-rata-rataPerintah kompilasi yang berhasil tidak menampilkan apa pun, dan menjalankan hasilnya dengan ./nilai-rata-rata akan mencetak keluaran yang sama dengan blok Keluaran di atas. Bila jumlah elemen tidak ingin dituliskan dua kali, idiom sizeof dapat menghitungnya dari array itu sendiri. sizeof nilai bernilai 20 byte, dan sizeof nilai[0] bernilai 4 byte, sehingga hasil baginya adalah lima elemen.
Perulangan yang sama juga dapat berjalan mundur dari indeks terakhir bila urutan cetak tidak penting, tetapi urutan maju tetap menjadi pilihan baku karena selalu berada di dalam batas. Yang perlu dijaga hanya kondisi berhentinya, karena kondisi yang meleset satu langkah saja sudah menyentuh elemen yang tidak ada.
// tidak-dijalankan
size_t jumlahElemen = sizeof nilai / sizeof nilai[0]; /* bernilai 5 */Tidak ada pemeriksaan batas pada saat runtime#
C tidak menyimpan ukuran array bersama datanya dan tidak memeriksa indeks pada saat runtime. Tanggung jawab menjaga indeks tetap berada di dalam batas sepenuhnya ada pada kode yang ditulis. Akibatnya, program dengan indeks keluar batas tetap terkompilasi dan tetap berjalan, tetapi perilakunya menjadi tidak terdefinisi (undefined behavior). Perilaku tidak terdefinisi berarti tidak ada jaminan apa pun tentang hasilnya, mulai dari membaca nilai kotor, mengubah variabel lain, hingga program berhenti dengan error.
// tidak-dijalankan
int a[3] = {7, 8, 9};
printf("%d\n", a[3]); /* indeks terakhir yang sah adalah 2 */
a[10] = 1; /* menulis jauh di luar array */Kompilator dapat membantu untuk kasus yang indeksnya konstan. Program berikut sengaja membaca elemen indeks 5 dari array berukuran lima, dan potongan ini tidak dijalankan karena perilakunya tidak terdefinisi.
// tidak-dijalankan
#include <stdio.h>
/* Program ini sengaja membaca di luar batas array.
Jangan dijalankan: perilakunya tidak terdefinisi. */
int main(void) {
int skor[5] = {10, 20, 30, 40, 50};
printf("%d\n", skor[5]);
return 0;
}# menangkap akses di luar batas berindeks konstan lewat peringatan kompilator
gcc -Wall -Wextra -O2 oob.c -o ooboob.c: In function ‘main’:
oob.c:9:5: warning: array subscript 5 is above array bounds of ‘int[5]’ [-Warray-bounds=]
9 | printf("%d\n", skor[5]);
| ^~~~~~~~~~~~~~~~~~~~~~~
oob.c:7:9: note: while referencing ‘skor’
7 | int skor[5] = {10, 20, 30, 40, 50};
| ^~~~Peringatan di atas dihasilkan gcc 14.2 pada tingkat optimasi -O2. Analisis semacam ini bergantung pada optimasi dan tidak mampu menangkap indeks yang berasal dari variabel. Peringatan itu berperan sebagai jaring pengaman tambahan, dan bukan pengganti disiplin menjaga batas.
Kesalahan umum#
- Memakai kondisi
i <= 5untuk array berukuran lima. Perulangan semacam itu menyentuhnilai[5]yang tidak ada, dan perbaikannya adalah mengganti kondisi menjadii < 5. Kesalahan satu langkah semacam ini dikenal sebagai off-by-one. - Menganggap elemen pertama berindeks 1. Indeks array C selalu dimulai dari 0, sehingga pencarian elemen terakhir dan perhitungan posisi tengah perlu mengikuti konvensi ini.
- Mengandalkan bahasa untuk memeriksa batas. Tidak ada pemeriksaan runtime di C, sehingga batas perulangan hanya dapat diverifikasi dengan membaca kode, sedangkan peringatan kompilator hanya menangkap sebagian kasus.
- Mengira ukuran array dapat berubah setelah deklarasi. Ukuran array tetap sejak proses kompilasi, sehingga kebutuhan data yang jumlahnya baru diketahui saat program berjalan ditangani memori dinamis dan bukan dengan memperbesar array.
- Menuliskan ukuran yang tidak cocok dengan jumlah nilai awalnya. Daftar nilai awal yang lebih panjang dari ukuran yang dituliskan ditolak kompilator, dan bentuk
int nilai[]tanpa ukuran menghilangkan kemungkinan salah hitung ini.
Ringkasan#
- Array satu dimensi dideklarasikan dengan tipe, nama, dan jumlah elemen yang tetap, misalnya
int nilai[5];, dan isinya dapat diinisialisasi sekaligus dengan tanda kurung kurawal. - Indeks berjalan dari 0 sampai jumlah elemen dikurangi satu, dan penelusuran memakai perulangan
fordengan kondisii < jumlah. - C tidak memeriksa batas pada saat runtime, sehingga akses di luar batas berujung pada perilaku tak terdefinisi.
- Idiom
sizeof nilai / sizeof nilai[0]menghitung jumlah elemen, tetapi hanya berlaku pada kode yang masih melihat array aslinya.
Referensi#
- Array declaration — cppreference.com — Deklarasi array C, aturan ukurannya, dan hubungannya dengan pointer.
- Options to Request or Suppress Warnings — GCC — Daftar opsi peringatan gcc, termasuk -Wall, -Wextra, dan -Warray-bounds.