@msc memberikan pengantar yang baik tentang aturan di balik perilaku ini.
Saya perhatikan bahwa jika saya mendeklarasikan variabel global beberapa kali, kompiler bahkan tidak mengeluarkan peringatan.
C memiliki tiga jenis deklarasi global untuk objek, yaitu yang (dan saya paham di static
sini):
- deklarasi yang bukan definisi -
extern int a;
- deklarasi yang juga definisi -
int a = 3;
atauextern int a = 3;
- definisi tentatif -
int a;
Deklarasi berganda tipe 1 & 3 diizinkan, sementara paling banyak satu (tipe 2) definisi diizinkan.
Apa penjelasan untuk perilaku ini?
Jika Anda juga bertanya tentang motivasi untuk aturan ini, itu adalah dukungan untuk kompilasi terpisah . (Lihat unit terjemahan ).
Untuk memecah suatu program menjadi beberapa file yang dikompilasi secara terpisah, kita memerlukan beberapa fitur, yaitu (a) dapat mendeklarasikan tanpa harus mendefinisikan , dan, (b) meneruskan deklarasi .
Dalam satu unit terjemahan, kami harus dapat merujuk ke fungsi global dan data dalam unit terjemahan lain. Dan kami juga ingin memeriksa kesalahan, di sini, untuk menemukan definisi yang hilang, dan definisi duplikat yang salah.
Terkadang, di unit terjemahan yang sama, kami mendeklarasikan global, dan kemudian mendefinisikannya nanti. Ini dapat terjadi jika kita memerlukan deklarasi maju untuk beberapa alasan, atau jika kita menggunakan file header umum (yang menyediakan deklarasi) dalam satu unit terjemahan yang juga menawarkan definisi eksplisit.
Karena kompilasi terpisah dalam C berlaku dengan menghubungkan fungsi dan data global bersama-sama, fitur-fitur ini diperlukan di tingkat global tetapi tidak di tingkat lokal.
Seperti yang ditunjukkan oleh @msc, semua ini tidak diperlukan untuk variabel lokal karena mereka tidak memiliki tautan.
C (seperti banyak bahasa lain) tidak menyediakan keterkaitan untuk variabel lokal karena bahasa tidak berusaha untuk mendukung fungsi tunggal yang mencakup beberapa unit terjemahan terpisah.
(Tentu saja, Anda dapat memiliki fungsi span beberapa file sumber, tetapi tidak beberapa unit terjemahan.)
Definisi tentatif berfungsi seperti deklarasi karena diizinkan dalam beberapa unit terjemahan (dan juga menggabungkan dengan baik dengan deklarasi lain). Namun, jika tidak ada definisi (non-tentatif) untuk pengidentifikasi di seluruh program, himpunan (satu atau lebih) definisi tentatif di beberapa unit terjemahan (untuk satu pengidentifikasi) diambil sebagai definisi untuk objek yang penginisialisasinya adalah nol.
Ini dapat diimplementasikan dengan menempatkan ini ke dalam bagian .BSS dengan ukuran dan keselarasan yang tepat; linker akan mencocokkannya dengan definisi sebenarnya jika ditemukan, atau mencocokkannya satu sama lain, memberi mereka ruang kosong di BSS.
Gagasan kompilasi yang terpisah dapat didukung sepenuhnya tanpa fitur definisi tentatif - Saya pikir definisi tentatif ada sebagian besar karena alasan historis. (Saya tidak mengatakan mereka tidak berguna, hanya jika bahasa itu dibuat hari ini, ini mungkin dianggap tidak perlu dan karenanya tidak ditawarkan.)