Keyword static pada deklarasi variabel atau fungsi di tingkat file (di luar fungsi) mengubah linkage dari external menjadi internal. Artinya, simbol tersebut hanya terlihat di dalam translation unit tempat ia didefinisikan—linker tidak akan mengeksposnya ke file .c lain. Fasilitas ini adalah mekanisme utama encapsulation di C: kita dapat menyembunyikan helper internal dan state privat modul tanpa khawatir tabrakan dengan nama serupa di file lain. Pada chapter ini kita akan mempraktikkan static di tingkat file, memverifikasi bahwa dua file .c dapat memiliki variabel static bernama sama tanpa konflik, dan memeriksa tabel simbol hasil kompilasi via nm.
Mekanisme Internal Linkage #
Saat compiler melihat deklarasi static int counter = 0; di tingkat file, compiler menempatkan variabel counter di BSS section (jika tidak diinisialisasi) atau data section (jika diinisialisasi), dan menandai simbol sebagai local di tabel simbol. Linker kemudian mengabaikan simbol tersebut saat menyatukan object file—simbol tidak akan terlihat dari file lain. Konsekuensinya, dua file .c dapat memiliki variabel static bernama sama persis (mis. counter) tanpa konflik; keduanya adalah variabel yang berbeda di memori.
Hal yang sama berlaku untuk fungsi: static void helper(void) { ... } hanya dapat dipanggil dari dalam file .c tempat ia didefinisikan. Fungsi static tidak dapat dipanggil dari file .c lain bahkan jika kita menambahkan deklarasi manual di file lain—linker tidak akan menemukan simbolnya. Sifat ini sangat berguna untuk menyembunyikan helper internal dari API publik modul.
Demonstrasi: Dua File dengan static counter #
Mari kita buat dua file .c yang masing-masing memiliki variabel static int counter = 0;. Kedua file memiliki fungsi publik tambah_a dan tambah_b yang masing-masing menginkrementasi counternya sendiri. File main.c memanggil kedua fungsi dan kita amati bahwa kedua counter bersifat independen.
File counter_a.c:
#include <stdio.h>
/* static: counter ini hanya dikenal di file counter_a.c */
static int counter = 0;
void tambah_a(void) {
counter++;
printf("[A] counter = %d\n", counter);
}File counter_b.c:
#include <stdio.h>
/* static: counter ini hanya dikenal di file counter_b.c */
static int counter = 0;
void tambah_b(void) {
counter++;
printf("[B] counter = %d\n", counter);
}File main.c:
/* Header sederhana untuk fungsi publik */
void tambah_a(void);
void tambah_b(void);
int main(void) {
tambah_a();
tambah_a();
tambah_b();
tambah_a();
tambah_b();
tambah_b();
return 0;
}Kompilasi dan jalankan:
gcc -std=c11 -Wall -Wextra -o app main.c counter_a.c counter_b.c && ./appOutput yang Dihasilkan:
[A] counter = 1
[A] counter = 2
[B] counter = 1
[A] counter = 3
[B] counter = 2
[B] counter = 3Perhatikan bahwa counter di counter_a.c mencapai 3 (dipanggil 3 kali), sementara counter di counter_b.c mencapai 3 terpisah (dipanggil 3 kali). Keduanya benar-benar variabel yang berbeda di memori meskipun namanya sama persis. Tanpa keyword static, linker akan melaporkan multiple definition untuk counter.
Mari kita telaah baris-baris kunci di atas:
- Pada
counter_a.cbaris 4 (static int counter = 0;): deklarasi denganstatic. Variabelcounterdialokasikan di data section dan ditandai sebagai simbol lokal. Filecounter_b.cdapat mendefinisikan variabelstaticdengan nama yang sama tanpa konflik. - Pada
counter_a.cbaris 6 (void tambah_a(void)): fungsi tanpastaticdi tingkat file—otomatis memiliki external linkage. Fungsi ini terlihat oleh linker dan dapat dipanggil darimain.c. Inilah API publik modulcounter_a. - Pada
counter_b.cbaris 4 (static int counter = 0;): variabelcounteryang sama sekali berbeda dengan yang dicounter_a.c. Walaupun namanya sama, internal linkage membuat kedua variabel terpisah di memori. - Pada
main.cbaris 2-3 (void tambah_a(void); void tambah_b(void);): deklarasi fungsi publik. Tidak perluexternkarena deklarasi fungsi (tanpa badan) implisitextern. Kita juga tidak perlu meng-include header—pendeklarasian langsung dimain.cvalid meski praktik yang lebih baik adalah mengumpulkan deklarasi publik di header bersama. - Pola pemanggilan: urutan
tambah_a() -> tambah_a() -> tambah_b() -> tambah_a() -> tambah_b() -> tambah_b()menghasilkan kenaikan terpisah untuk masing-masing counter. Counter[A]naik tiga kali menjadi3, counter[B]naik tiga kali menjadi3—tetapi melalui jalur yang berbeda di memori.
Memverifikasi dengan nm #
Untuk meyakinkan diri bahwa static benar-benar mengubah linkage, kita dapat memeriksa tabel simbol hasil kompilasi menggunakan tool nm. Kompilasi masing-masing file .c menjadi object file:
gcc -std=c11 -Wall -Wextra -c counter_a.c -o counter_a.o
gcc -std=c11 -Wall -Wextra -c counter_b.c -o counter_b.o
nm counter_a.oOutput nm counter_a.o:
0000000000000000 b counter
U printf
0000000000000000 T tambah_aOutput nm counter_b.o:
0000000000000000 b counter
U printf
0000000000000000 T tambah_bMari kita interpretasikan simbol-simbol di atas:
b counter: huruf kecilbberarti simbol berada di BSS section (data tidak diinisialisasi) dengan local linkage (internal). Inilah efekstatic. Linker tidak akan mengekspos simbol ini ke file lain.T tambah_adanT tambah_b: huruf besarTberarti simbol di text section dengan external linkage. Linker mengizinkan kedua fungsi ini dirujuk dari object file lain.U printf: hurufUberarti simbol undefined—counter_a.omembutuhkanprintftetapi tidak mendefinisikannya. Linker akan mencariprintfdi pustaka standarlibcdan mencocokkannya saat linking.
Verifikasi nm ini sangat berguna untuk memastikan bahwa keyword static bekerja sesuai harapan. Dalam proyek nyata, menjalankan nm pada object file membantu memverifikasi bahwa kita tidak tanpa sengaja mengekspos simbol yang seharusnya internal.
Penggunaan Praktis static #
Beberapa pola umum yang memanfaatkan static di tingkat file:
- Helper internal: fungsi
statichanya dipanggil dalam satu file.cdan tidak boleh muncul di API publik. Contoh:static int validate_input(int x)yang dipakai oleh beberapa fungsi publik di modul. - State privat modul: variabel
staticyang menyimpan state yang hanya boleh diakses via fungsi publik. Contoh:static int pool_size = 0;dipool.cyang diakses viapool_alloc()danpool_free(). - Konstanta internal: array lookup atau tabel yang hanya dipakai di satu file. Contoh:
static const char *month_names[] = {"Jan", "Feb", ...};didate.c.