Kelas Penyimpanan

Kelas penyimpanan auto, static, extern, dan register dilihat dari durasi, lingkup, dan linkage.

Daftar isi

Kelas penyimpanan (storage class) adalah sifat yang ditetapkan pada saat deklarasi dan menentukan tiga hal sekaligus, yaitu durasi penyimpanan variabel, lingkup nama, dan linkage. Linkage mengatur apakah nama yang sama dapat dihubungkan antarberkas. Topik ini membahas empat kata kuncinya, yaitu auto, register, static, dan extern, lengkap dengan contoh yang dapat dijalankan. Setelah topik ini dipahami, pilihan kata kunci dapat dipertimbangkan dari sifat yang dibutuhkan, bukan dari kebiasaan menyalin kode lama.

Durasi, lingkup, dan linkage#

Durasi (storage duration) menyatakan berapa lama memori variabel dipertahankan, yaitu otomatis mengikuti blok, atau statis sepanjang program. Lingkup menyatakan daerah kode yang mengenal namanya, sedangkan linkage menyatakan apakah nama yang sama pada berkas lain merujuk objek yang sama atau tidak. Tabel berikut meringkas empat kelas penyimpanan dengan ketiga sifatnya.

KelasKata kunciDurasiLingkupLinkage
Lokal biasaauto implisit atau registerotomatisbloktidak ada
Statis di blokstaticstatisbloktidak ada
Statis di berkasstaticstatisberkasinternal
Eksternalextern atau tanpa kata kuncistatisberkaseksternal

Tanpa kata kunci apa pun, variabel di dalam fungsi berperilaku auto dan variabel di luar fungsi berperilaku eksternal. Dua baris pertama pada tabel berbagi lingkup blok tetapi berbeda durasi, sedangkan dua baris terakhir berbagi durasi statis tetapi berbeda jangkauan namanya. Kata kunci static yang sama punya arti berbeda bergantung posisi deklarasinya, sehingga kedua artinya harus dibedakan dari konteksnya. Pemahaman ketiga sifat tersebut membuat arti kata kunci pada setiap posisi dapat disimpulkan tanpa menghafal.

static: nilai yang bertahan antarpanggilan#

Kata kunci static pada variabel lokal mengubah durasinya menjadi statis, sehingga memori variabel dipertahankan sepanjang program meskipun lingkup namanya tetap sebatas blok. Inisialisasi yang dituliskan hanya dilakukan satu kali pada awal program, bukan pada setiap pemanggilan. Perilaku ini cocok untuk penghitung dan penanda keadaan yang harus mengingat panggilan sebelumnya.

c penyimpanan-static.c
#include <stdio.h>

int panggilan_ke(void)
{
    static int jumlah = 0;    /* diinisialisasi satu kali saja */

    jumlah = jumlah + 1;
    return jumlah;
}

int main(void)
{
    printf("Pemanggilan ke-%d\n", panggilan_ke());
    printf("Pemanggilan ke-%d\n", panggilan_ke());
    printf("Pemanggilan ke-%d\n", panggilan_ke());
    return 0;
}
text Keluaran
Pemanggilan ke-1
Pemanggilan ke-2
Pemanggilan ke-3

Tiga pemanggilan menghasilkan tiga nilai berbeda karena jumlah tidak pernah dibuat ulang, berbeda dari variabel lokal biasa yang selalu berawal dari nilai awalnya. Meskipun bertahan lama, jumlah tetap tidak dikenali di luar panggilan_ke, karena penambahan durasi tidak mengubah lingkup sama sekali. Kombinasi inilah yang membedakan static lokal dari variabel global, yaitu nilai yang bertahan selama program berjalan tetapi aksesnya terkunci pada satu fungsi.

Variabel static lokal juga harus diberi nilai awal berupa konstanta, karena inisialisasinya dikerjakan sekali pada awal program sebelum masukan apa pun tersedia. Apabila nilai awalnya bergantung pada perhitungan, cukup tuliskan pernyataan penugasan pertama di dalam fungsi. Pola ini mempertahankan jaminan bahwa nilai awal hanya dipasang sekali.

static dan extern pada tingkat berkas#

Pada tingkat berkas, static berubah makna menjadi pembatas linkage. Fungsi atau variabel global yang diberi static hanya dapat dihubungkan di dalam berkasnya, sehingga namanya terlindungi dari berkas lain yang kebetulan memakai nama sama. Sifat ini dipakai untuk menyembunyikan fungsi bantu yang tidak dimaksudkan untuk dipanggil dari luar.

c Contoh: fungsi bantu dengan linkage internal
// tidak-dijalankan
static int pembulatan_ke_bawah(double x)
{
    return (int)x;            /* hanya dikenali di berkas ini */
}

Kata kunci extern berada di sisi sebaliknya, yaitu melaporkan bahwa sebuah nama didefinisikan di tempat lain sehingga berkas ini boleh memakainya. Deklarasi extern tidak menyediakan memori, melainkan menjanjikan bahwa definisinya pasti tersedia pada saat pengaitan. Program berikut mendeklarasikan variabel global di bagian atas dan mendefinisikannya di bagian bawah. Pola yang sama dipakai ketika definisi berada di berkas lain.

c penggunaan-extern.c
#include <stdio.h>

/* Deklarasi extern: definisinya berada di akhir berkas ini. */
extern int total_transaksi;

void catat(int nilai)
{
    total_transaksi = total_transaksi + nilai;
}

int main(void)
{
    catat(12000);
    catat(8000);
    printf("Total transaksi: %d\n", total_transaksi);
    return 0;
}

/* Definisi variabel yang dijanjikan deklarasi extern di atas. */
int total_transaksi = 0;
text Keluaran
Total transaksi: 20000

Deklarasi extern pada bagian atas membuat catat dan main mengenal variabel itu lebih dahulu, sementara definisinya menyediakan memori sekaligus nilai awalnya. Pada proyek berbanyak berkas, deklarasi semacam ini ditempatkan di header, sedangkan definisinya hanya ditulis pada satu berkas .c saja. Pola tersebut langsung terasa manfaatnya ketika kode mulai dibagi ke beberapa berkas.

auto dan register#

Kata kunci auto menandai durasi otomatis, tetapi sifat itu sudah menjadi default bagi setiap variabel lokal, sehingga penulisannya hampir tidak pernah diperlukan. Standar C23 bahkan memberi kata kunci ini makna baru untuk penarikan tipe, sehingga menuliskannya secara eksplisit makin tidak disarankan. Kata kunci register menyarankan agar variabel disimpan pada register prosesor demi kecepatan, dengan konsekuensi alamatnya tidak dapat diambil melalui operator &.

c Contoh: register pada variabel penghitung
// tidak-dijalankan
int jumlahkan_hingga(int batas)
{
    register int i;
    int total = 0;

    for (i = 0; i < batas; i = i + 1) {
        total = total + i;
    }
    return total;
}

Kompilator modern menentukan penempatan register jauh lebih baik daripada petunjuk manual, sehingga register kini jarang ditulis dan sering dianggap warisan sejarah. Kedua kata kunci tetap perlu dikenali karena masih muncul pada kode lama dan pada tabel kelas penyimpanan standar. Keduanya tidak mengubah lingkup maupun linkage, hanya penempatan penyimpanannya.

Konsekuensi yang perlu diingat dari register adalah larangan mengambil alamat variabelnya melalui operator &. Alamat variabel biasa dapat dioper kepada fungsi lain, sedangkan variabel register tidak menjamin penyimpanan pada memori. Larangan ini menjadi satu alasan lagi untuk tidak menuliskan register tanpa kebutuhan nyata.

Kesalahan umum#

Sifat kelas penyimpanan paling sering salah dipahami pada empat titik berikut, dan semuanya menyeret perilaku program ke arah yang tidak diharapkan. Setiap butir dapat diuji cepat dengan mengompilasi ulang contoh pada topik ini.

  • Memberi static lokal nilai awal yang tidak tetap. Inisialisasi variabel static harus berupa ekspresi konstanta, sehingga static int n = batas; ditolak kompilator. Hitung nilai tersebut pada pernyataan terpisah setelah deklarasi.
  • Mengira static lokal kembali ke nilai awal setiap pemanggilan. Inisialisasinya hanya dilakukan satu kali, sedangkan nilai berikutnya bertahan dari panggilan sebelumnya. Perlakukan variabel tersebut seperti memori kecil milik fungsi.
  • Memakai extern tanpa definisi di mana pun. Kompilasi berhasil karena janjinya dipercaya, tetapi pengaitan berhenti dengan pesan undefined reference. Pastikan tepat satu berkas mendefinisikan variabel yang dijanjikan.
  • Mengira register mempercepat program secara otomatis. Petunjuk itu sering diabaikan kompilator modern, sedangkan larangan mengambil alamatnya tetap berlaku. Serahkan penempatan register kepada kompilator dan kenali kata kunci ini hanya untuk membaca kode lama.

Ringkasan#

  • Kelas penyimpanan menetapkan durasi, lingkup, dan linkage sekaligus, sehingga satu kata kunci dapat mengubah lebih dari satu sifat.
  • static pada variabel lokal mempertahankan nilai antarpanggilan, sedangkan pada tingkat berkas membatasi nama pada berkasnya sendiri melalui linkage internal.
  • extern menjanjikan definisi di tempat lain sehingga nama dapat dipakai sebelum definisinya ditemukan, dan janji itu ditegaskan pada saat pengaitan.
  • auto dan register kini jarang ditulis karena sifat otomatis sudah menjadi default dan penempatan register lebih baik diserahkan kepada kompilator.

Referensi#