Bagaimana kompiler c ++ menemukan variabel eksternal?

15

Saya mengkompilasi program ini dengan g ++ dan clang ++. Ada perbedaan:
g ++ mencetak 1, tetapi dentang ++ mencetak 2.
Tampaknya
g ++: extern varible didefinisikan dalam lingkup terpendek.
dentang ++: variabel ekstern didefinisikan dalam ruang lingkup global terpendek.

Apakah spesifikasi C ++ memiliki spesifikasi tentang itu?

main.cpp

#include <iostream>
static int i;
static int *p = &i;

int main() {
  int i;
  {
    extern int i;
    i = 1;
    *p = 2;
    std::cout << i << std::endl;
  }
}

other.cpp

int i;

versi: g ++: 7.4.0 / clang ++:
kompilasi 10.0.0 : $ (CXX) main.cpp other.cpp -o extern.exe

eddie kuo
sumber
4
Compiler tidak melakukan apa pun dengan extern kecuali menandainya sebagai variabel yang memiliki referensi eksternal, linker adalah apa yang mencoba untuk menyelesaikan tautan di antara semua file objek yang dikompilasi.
SPlatten
Pertanyaan yang sangat bagus (jika aneh)! Bermain-main dengan kode Anda di MSVCdan clang-cl(keduanya memberi 2), tampaknya extern int ibenar-benar diabaikan oleh keduanya: bahkan jika saya tidak menautkan dalam other.cppfile, program membangun dan berjalan.
Adrian Mole
1
@ Slatten Mungkin, karena penghubung tidak perlu 'menyelesaikan' referensi i, itu tidak mencoba.
Adrian Mole
3
Bug GCC lama yang ditangguhkan terkait dapat ditemukan di sini dan bug Dentang terbuka yang sesuai di sini
walnut

Jawaban:

11

[basic.link/7] harus menjadi bagian yang relevan dari Standar ini. Dalam draft saat ini, tertulis:

Nama fungsi yang dideklarasikan dalam lingkup blok dan nama variabel yang dideklarasikan oleh externdeklarasi lingkup blok memiliki keterkaitan. Jika deklarasi semacam itu dilampirkan ke modul bernama, program ini salah bentuk. Jika ada deklarasi yang terlihat dari suatu entitas dengan linkage, mengabaikan entitas yang dideklarasikan di luar lingkup namespace terlampir, sehingga deklarasi lingkup blok akan menjadi (mungkin tidak terbentuk) deklarasi ulang jika dua deklarasi muncul di wilayah deklaratif yang sama, blokir deklarasi menyatakan entitas yang sama dan menerima tautan dari deklarasi sebelumnya. Jika ada lebih dari satu entitas yang cocok seperti itu, program ini salah bentuk. Jika tidak, jika tidak ada entitas yang cocok ditemukan, entitas lingkup blok menerima tautan eksternal.Jika, dalam unit terjemahan, entitas yang sama dideklarasikan dengan hubungan internal dan eksternal, program tersebut salah bentuk.

Perhatikan bahwa contoh berikut hampir sama persis dengan kasus Anda:

static void f();
extern "C" void h();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: external linkage, ill-formed
  }
}

Jadi, programnya harus dibuat-buat. Penjelasannya di bawah contoh:

Tanpa deklarasi di baris # 2, deklarasi di baris # 3 akan terhubung dengan deklarasi di baris # 1. Karena deklarasi dengan tautan internal disembunyikan, namun, # 3 diberi tautan eksternal, membuat program tersebut salah bentuk.

Daniel Langr
sumber
Program dalam contoh ini salah bentuk karena tidak ada i dengan tautan eksternal yang ditentukan di mana saja. Ini tidak terjadi dengan contoh OP.
n. 'kata ganti' m.
3
@ n.'pronouns'm. Tetapi aturan tersebut berlaku untuk unit terjemahan: Jika, di dalam unit terjemahan, entitas yang sama dinyatakan dengan hubungan internal dan eksternal, program tersebut salah bentuk. .
Daniel Langr
2
Jawabannya hanya berlaku untuk C ++ 17 dan yang lebih baru, lihat resolusi masalah CWG 426 . Sepertinya saya bahwa GCC benar sebelum perubahan itu.
walnut
OK sepertinya saya membaca edisi standar sebelumnya.
n. 'kata ganti' m.