Standar C99 menambahkan sejumlah fasilitas yang membuat kode lebih ringkas tanpa mengorbankan sifat C yang dekat dengan mesin. Tiga yang paling sering dipakai adalah array dengan panjang variabel (variable length array atau VLA), inisialisasi dengan penunjuk anggota (designated initializer), dan literal majemuk (compound literal). Topik ini membahas ketiganya melalui contoh yang dapat dijalankan, ditutup dengan catatan tentang kapan masing-masing layak dipakai. Kompilator gcc dengan pengaturan bawaannya sudah mengaktifkan seluruh fitur C99 ini, sehingga contohnya dapat langsung dikompilasi tanpa opsi tambahan.
Array dengan panjang variabel (VLA)#
Panjang array biasa harus diketahui saat kompilasi, sedangkan VLA menentukan panjangnya saat program berjalan melalui nilai sebuah variabel. Deklarasinya tidak memerlukan sintaks baru, karena ukurannya cukup ditulis sebagai variabel int yang sudah bernilai. VLA tersimpan seperti array biasa, yaitu pada stack, dan masa hidupnya berakhir ketika blok yang memuatnya selesai. Program berikut memakai VLA sebagai array lokal sekaligus sebagai parameter fungsi.
#include <stdio.h>
int jumlah(int n, int arr[n]) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
int main(void) {
int n = 5;
int nilai[n]; /* array dengan panjang variabel */
for (int i = 0; i < n; i++) {
nilai[i] = (i + 1) * 10;
}
printf("Panjang array: %d\n", n);
printf("Total nilai : %d\n", jumlah(n, nilai));
return 0;
}Panjang array: 5
Total nilai : 150Pada parameter fungsi, penulisan int arr[n] setelah parameter n menyatakan bahwa array itu memiliki panjang n. Bentuk ini menjadikan hubungan kedua parameter terbaca langsung pada tanda tangan fungsi, dan ekspresi seperti sizeof *arr di dalam fungsi juga menunjuk elemen dengan benar. Satu batasan perlu diingat: VLA tidak dapat diinisialisasi pada deklarasinya, karena panjangnya baru diketahui ketika program berjalan. Isinya harus diisi dengan penugasan atau perulangan seperti pada contoh.
Designated initializer#
Designated initializer menunjuk secara eksplisit anggota mana yang diinisialisasi, dengan penulisan .nama_anggota = nilai untuk struct dan [indeks] = nilai untuk array. Anggota atau elemen yang tidak disebut diisi nol secara otomatis, dan urutan penulisan penunjuknya bebas. Program berikut memakai keduanya.
#include <stdio.h>
struct Pengaturan {
int lebar;
int tinggi;
int skala;
};
int main(void) {
int hitung[6] = {[1] = 10, [3] = 30, [4] = 40};
for (int i = 0; i < 6; i++) {
printf("hitung[%d] = %d\n", i, hitung[i]);
}
struct Pengaturan p = {.skala = 2, .lebar = 640, .tinggi = 480};
printf("Layar: %dx%d, skala %d\n", p.lebar, p.tinggi, p.skala);
return 0;
}hitung[0] = 0
hitung[1] = 10
hitung[2] = 0
hitung[3] = 30
hitung[4] = 40
hitung[5] = 0
Layar: 640x480, skala 2Elemen 0, 2, dan 5 yang tidak disebut otomatis bernilai 0, sehingga pola ini praktis untuk tabel jarang (sparse table) yang hanya mengisi beberapa posisi. Pada struct, inisialisasi lewat nama anggota lebih tahan terhadap perubahan kode dibandingkan urutan nilai polos, karena penambahan anggota baru di tengah struct tidak merusak makna inisialisasinya. Inilah alasan struct bertipe Pengaturan di atas ditulis dengan penunjuk meskipun seluruh anggotanya diisi.
Pola ini juga lazim pada array bertipe struct, misalnya struct Siswa daftar[3] = {[0] = {.nilai = 90}, [2] = {.nilai = 75}}; yang hanya mengisi dua siswa dan membiarkan siswa tengah kosong. Penulisan bersarang semacam itu menggabungkan penunjuk indeks array dengan penunjuk anggota struct dalam satu inisialisasi. Kombinasi keduanya menggantikan rangkaian penugasan panjang yang biasanya ditulis setelah deklarasi.
Compound literal#
Compound literal adalah ekspresi yang membuat objek tanpa nama dari sebuah tipe. Bentuknya berupa tanda kurung tipe diikuti tanda kurung kurawal berisi nilai, misalnya (struct Titik){.x = 3, .y = 4}. Bentuk ini mengisi parameter bertipe struct atau menyediakan array tanpa perlu mendeklarasikan variabel perantara. Program berikut memakai kedua bentuknya.
#include <stdio.h>
struct Titik {
int x;
int y;
};
void cetak_titik(struct Titik t) {
printf("(%d, %d)\n", t.x, t.y);
}
int main(void) {
cetak_titik((struct Titik){.x = 3, .y = 4});
int *nilai = (int[]){10, 20, 30, 40};
int total = 0;
for (int i = 0; i < 4; i++) {
total += nilai[i];
}
printf("Total: %d\n", total);
return 0;
}(3, 4)
Total: 100Pemanggilan cetak_titik pada contoh tidak memerlukan variabel struct Titik terpisah, karena literal majemuknya langsung menjadi argumen. Bentuk (int[]){10, 20, 30, 40} membuat array tanpa nama yang dapat ditunjuk pointer. Compound literal juga dapat dipadukan dengan designated initializer, sebagaimana terlihat pada penulisan .x dan .y. Masa hidup compound literal di dalam sebuah blok mengikuti blok tersebut, sehingga alamatnya tidak boleh disimpan untuk dipakai setelah blok berakhir.
Pointer yang menunjuk compound literal dapat dipakai layaknya pointer ke variabel biasa, termasuk menulis melaluinya, selama masa hidupnya belum berakhir. Kesalahan yang sering terjadi adalah menyimpan alamat literal yang dibuat di dalam blok, lalu memakainya setelah blok selesai. Kejadian ini sama persis dengan pengembalian alamat variabel lokal. Selama pemakaiannya tetap berada di blok yang sama, bentuk ini aman dan menghemat satu deklarasi.
Kapan ketiganya dipakai#
Ketiga fitur ini menyelesaikan masalah yang berbeda, sehingga pilihannya mengikuti kebutuhan, bukan selera. Tabel berikut merangkum situasi yang cocok untuk masing-masing.
| Fitur | Situasi yang cocok | Catatan |
|---|---|---|
| VLA | array sementara yang ukurannya berasal dari masukan | tidak dapat diinisialisasi, ukuran besar membahayakan stack |
| Designated initializer | struct dengan anggota opsional dan tabel array yang jarang diisi | yang tidak disebut otomatis bernilai nol |
| Compound literal | mengirim struct atau array tanpa variabel perantara | masa hidupnya mengikuti blok tempat ia ditulis |
VLA paling tepat untuk data sementara berukuran kecil yang panjangnya baru diketahui saat program berjalan, sedangkan kebutuhan ukuran besar lebih aman dialihkan ke malloc. Designated initializer meningkatkan ketahanan kode terhadap perubahan definisi tipe, dan compound literal menghapus deklarasi yang hanya dipakai satu kali. Ketiganya sering tampil bersama, misalnya compound literal bertipe struct yang sekaligus diinisialisasi dengan penunjuk anggota.
Keputusan lain yang perlu dipertimbangkan adalah tingkat dukungan platform. Standar C11 menjadikan VLA fitur opsional, ditandai makro __STDC_NO_VLA__ apabila kompiler tidak menyediakannya, sedangkan dua fitur lainnya wajib ada pada setiap kompiler C99. Kode yang menyasar perangkat tertanam dengan kompiler lawas perlu memeriksa kondisi tersebut sebelum memakai VLA.
Kesalahan umum#
- Mengira anggota yang tidak disebut mempertahankan nilai lama. Designated initializer mengisi ulang seluruh objek pada deklarasinya, dan anggota yang tidak disebut selalu bernilai nol, bukan bernilai dari penugasan sebelumnya. Pola isi-dulu-lalu-ubah tetap ditulis sebagai penugasan biasa setelah deklarasinya, dan kaidah yang sama berlaku pada elemen array yang tidak disebut.
- Menginisialisasi VLA pada deklarasinya. Penulisan
int nilai[n] = {0};ditolak kompiler dengan pesan bahwa objek berukuran variabel tidak dapat diinisialisasi. Isi VLA melalui penugasan atau perulangan setelah deklarasinya. - Membiarkan ukuran VLA bernilai nol atau negatif. Panjang nol maupun negatif merupakan perilaku tak terdefinisi, dan program dapat berhenti mendadak tanpa pesan yang menjelaskan. Periksa nilai ukurannya sebelum deklarasi, misalnya dengan
if. - Mengira inisialisasi campuran selalu mulai dari indeks nol. Pada
int a[5] = {1, [3] = 9, 7};, nilai 9 masuk ke indeks 3 dan nilai 7 berlanjut ke indeks 4, bukan ke indeks 1. Nilai tanpa penunjuk selalu melanjutkan dari indeks setelah penunjuk terakhir.
Ringkasan#
- VLA menentukan panjang array saat program berjalan, baik sebagai array lokal maupun sebagai parameter fungsi, tetapi tidak dapat diinisialisasi pada deklarasinya.
- Designated initializer menunjuk anggota struct atau indeks array secara eksplisit, dan yang tidak disebut otomatis bernilai nol.
- Compound literal membuat objek tanpa nama langsung di posisi pemakaiannya, dan masa hidupnya mengikuti blok tempat ia ditulis.
- Ketiganya fitur C99 yang aktif pada pengaturan bawaan gcc sehingga dapat dipakai tanpa opsi kompilasi tambahan.
Referensi#
- Array declaration — cppreference.com — Aturan array C, termasuk bagian khusus array dengan panjang variabel.
- Struct and union initialization — cppreference.com — Kaidah designated initializer untuk struct dan array.
- Compound literals — cppreference.com — Sintaks dan masa hidup literal majemuk pada C99.