Keyword extern adalah penanda eksplisit bahwa sebuah deklarasi variabel atau fungsi merujuk ke simbol yang didefinisikan di tempat lain—di file .c lain, di pustaka standar, atau di static library. Tanpa extern, deklarasi variabel di header bisa menjadi tentative definition yang memicu multiple definition saat di-include berkali-kali. Dengan extern, kita memberi tahu compiler: “variabel ini ada, tapi didefinisikan di file lain—cukup catat tipenya untuk pemeriksaan tipe.” Pada chapter ini kita akan mempraktikkan deklarasi extern di header, peran extern dalam pemanggilan fungsi lintas file, dan penggunaan extern di dalam badan fungsi.
Mekanisme extern #
Saat compiler melihat deklarasi extern int x;, compiler tidak mengalokasikan memori untuk variabel x. Compiler hanya mencatat tipe dan nama simbol ke dalam tabel simbol translation unit tersebut, dengan tanda bahwa definisi akan disediakan oleh translation unit lain. Saat linker menyatukan object file, linker mencari definisi x di object file lain dan mengisi alamat memori yang sebenarnya. Jika tidak ada definisi x di mana pun, linker melaporkan undefined reference.
Untuk fungsi, situasinya sedikit berbeda. Deklarasi fungsi tanpa badan (mis. int jumlah(int a, int b);) secara implisit bersifat extern—kita tidak perlu menulis extern di depan. Keyword extern pada deklarasi fungsi valid tetapi jarang dipakai karena redundant. Untuk variabel, extern sangat penting di header agar tidak menjadi tentative definition.
Demonstrasi: Variabel extern di Header #
Mari kita bangun modul pengatur yang menyimpan dua variabel global publik (max_user dan hitungan_aktif) serta tiga fungsi publik. Header pengatur.h berisi deklarasi extern untuk variabel dan deklarasi fungsi; file pengatur.c berisi definisi aktual variabel dan implementasi fungsi.
File pengatur.h:
#ifndef PENGATUR_H
#define PENGATUR_H
/* Deklarasi extern di header */
extern int max_user;
extern int hitungan_aktif;
/* Deklarasi fungsi (implisit extern) */
void pengatur_init(int max);
void pengatur_tambah(void);
int pengatur_get_hitungan(void);
#endifFile pengatur.c:
#include "pengatur.h"
/* Definisi aktual variabel extern -- hanya di sini */
int max_user = 10;
int hitungan_aktif = 0;
void pengatur_init(int max) {
max_user = max;
hitungan_aktif = 0;
}
void pengatur_tambah(void) {
if (hitungan_aktif < max_user) {
hitungan_aktif++;
}
}
int pengatur_get_hitungan(void) {
return hitungan_aktif;
}File main.c:
#include <stdio.h>
#include "pengatur.h"
int main(void) {
/* Akses variabel extern melalui header */
printf("Default max_user = %d, hitungan_aktif = %d\n",
max_user, hitungan_aktif);
pengatur_init(5);
for (int i = 0; i < 8; i++) {
pengatur_tambah();
}
printf("Setelah init(5) + 8x tambah: hitungan = %d, max_user = %d\n",
pengatur_get_hitungan(), max_user);
return 0;
}Kompilasi dan jalankan:
gcc -std=c11 -Wall -Wextra -I. -o app main.c pengatur.c && ./appOutput yang Dihasilkan:
Default max_user = 10, hitungan_aktif = 0
Setelah init(5) + 8x tambah: hitungan = 5, max_user = 5Perhatikan bahwa main.c dapat membaca dan menulis max_user walau variabel didefinisikan di pengatur.c. Fungsi pengatur_init(5) mengubah max_user menjadi 5; kemudian 8 kali pemanggilan pengatur_tambah() hanya menambah hitungan_aktif sampai 5 (karena ada batasan hitungan_aktif < max_user). Pada baris terakhir, max_user terbaca sebagai 5 dari main.c—sama dengan nilai yang diset oleh pengatur_init.
Mari kita telaah baris-baris kunci di atas:
- Pada
pengatur.hbaris 7-8 (extern int max_user;danextern int hitungan_aktif;): deklarasi tanpa definisi. Tidak ada alokasi memori. Keywordexternmemberitahu compiler bahwa definisi variabel ada di file.clain. Header boleh di-include berkali-kali tanpa menyebabkan multiple definition karena deklarasiexternmurni adalah forward declaration. - Pada
pengatur.hbaris 11-13 (deklarasi fungsi): tidak adaexterndi depan. Deklarasi fungsi tanpa badan otomatis bersifatextern. Compiler mengizinkan pemanggilan fungsipengatur_initdarimain.cmeskipun definisi ada di file lain. - Pada
pengatur.cbaris 4-5 (int max_user = 10;danint hitungan_aktif = 0;): definisi aktual. Inilah tempat variabel mendapat alamat memori. Definisi ini hanya boleh muncul sekali di seluruh proyek. Jika dua file.cmendefinisikanint max_user = 10;tanpastatic, linker akan melaporkan multiple definition. - Pada
main.cbaris 6 (printf("Default max_user = %d, hitungan_aktif = %d\n", max_user, hitungan_aktif);):main.cmembaca nilai variabelextern. Saat kompilasi, compiler hanya tahu tipeint—tidak tahu alamat. Saat linking, linker mencari definisimax_userdipengatur.odan menemukanint max_user = 10;. Alamat tersebut diisi ke instruksi mesin dimain.o. - Pada
main.cbaris 11 (pengatur_init(5);): pemanggilan fungsi publik yang mengubahmax_usermenjadi5. Setelah pemanggilan ini, nilaimax_userdimain.cjuga berubah menjadi5—bukti bahwamain.cdanpengatur.cberbagi variabel yang sama.
extern di Dalam Fungsi #
extern tidak harus muncul di header saja. Kita dapat menulis deklarasi extern di dalam badan fungsi untuk mengakses variabel global yang tidak dideklarasikan di header. Pola ini berguna untuk akses yang sangat lokal—mis. satu fungsi saja yang perlu membaca variabel internal modul lain.
#include <stdio.h>
/* extern declaration dari variabel yang didefinisikan di file lain */
extern int errno_demo; /* misal -- di proyek nyata: errno di <errno.h> */
/* Lebih umum: deklarasi extern fungsi tanpa keyword (implisit extern) */
int jumlah(int a, int b); /* implisit extern */
int main(void) {
/* Contoh lebih lengkap: dapat juga extern digunakan di dalam fungsi */
extern int pengaturan_global; /* definisi ada di file lain */
printf("pengaturan_global = %d\n", pengaturan_global);
return 0;
}
int jumlah(int a, int b) { return a + b; }
/* Definisi variabel global yang dapat di-extern dari file lain */
int pengaturan_global = 42;Output yang Dihasilkan:
pengaturan_global = 42Dalam contoh ini, fungsi main mendeklarasikan extern int pengaturan_global; di dalam badan fungsi. Definisi variabel tersebut berada di akhir file yang sama (int pengaturan_global = 42;), tetapi deklarasi extern di dalam fungsi tetap valid—compiler menyelesaikan referensi saat linking di tahap akhir. Pola seperti ini lebih umum ditemui di kode lama yang belum dipecah ke banyak file; di proyek modern, sebaiknya kumpulkan semua deklarasi extern di header bersama.