Jestem zdezorientowany z użyciem extern w tym samym pliku, jak pokazano w poniższym kodzie. Pierwszy przypadek był faktycznie rozwiązaniem do wydrukowania zmiennej globalnej w C (gdy istnieje sama nazwa zmienna lokalna), ale nie jestem w stanie zrozumieć, jak to działa i jak trzeci przypadek nie działał.

Przypadek 1:

int a = 10;
int main()
{
    int a = 20;
    {
        extern int a; // Is this telling the linker to use global variable? 
        printf("A value: %d\n", a);
    }
    return 0;
}

Przypadek 2:

extern int a; // If in the above case extern was telling linker to use global variable 
              // then how in this local variable is getting referred
int main()
{
    int a = 20;
    {
        printf("A value: %d\n", a);
    }
    return 0;
}

Przypadek 3:

// int a = 10;
int main()
{
    int a = 20;
    {
         extern int a; // Why now I get a linking error
         printf("A value: %d\n", a);
    }
    return 0;
}
4
Pranjal Srivastava 5 czerwiec 2018, 14:22

4 odpowiedzi

Najlepsza odpowiedź

W pierwszym przypadku masz globalny a, który zastępujesz z lokalnym (automatyczny) a, że ponownie nadpisujesz się z globalnym a (Extern może odnosić się tylko do zmiennych globalnych w niektórych module ). Wydrukuje 10.

W drugim przypadku masz globalną a, która znajduje się w tym lub w innym module (jednostka pliku / kompilacji C), którą zastępujesz z lokalnym a. Wydrukuje 20.

W trzecim przypadku masz lokalny a, który nadpisujesz z globalnym a, który najwyraźniej nie istnieje w żadnej z urządzeń kompilacyjnych, stąd błąd łącznika.

6
Paul Ogilvie 5 czerwiec 2018, 13:14

(Należy pamiętać, że edycje do kodu w pytaniu wydają się, że części tej odpowiedzi nie są już całkiem poprawne.)

Na 6.2.2 Podłączenia identyfikatorów , pkt 4 Standard C:

Aby uzyskać identyfikator zadeklarowany za pomocą specyfikatora klas przechowywania extern W zakresie, w którym widoczna jest wcześniejsza deklaracja tego identyfikatora, Jeśli wcześniejsza deklaracja określa wiązanie wewnętrzne lub zewnętrzne, Wiązanie identyfikatora w późniejszym deklaracji jest tak samo jak wiązanie określone w poprzedniej deklaracji.

Więc w pierwszych dwóch przypadkach, wewnętrzny extern int a; ma wcześniejsze deklaracja - globalny int a; w pierwszym przypadku lub {x2}} w drugim przypadku - więc deklarację extern int a; odnosi się do globalnego.

W trzeciej sprawie pozostała część ust. 4 jest istotna:

Jeśli nie jest widoczna żadna wcześniejsza deklaracja, czy wcześniej Deklaracja Określa nie dołączenia, a następnie Identyfikator ma zewnętrzne połączenie .

Również ustęp 6 państw:

Następujące identyfikatory nie mają połączenia: identyfikator zadeklarowany jest niczym innym niż obiekt lub funkcja; identyfikator zadeklarowany jest parametr funkcji; Zakres blokowy Identyfikator obiektu zadeklarowany bez specyfikatora klas przechowywania extern.

Deklaracja w trzeciej sprawie odnosi się do extern int a;

Nie ma jednak żadnego rzeczywistego int a; zdefiniowane w dowolnym miejscu. A ponieważ extern int a; w trzecim przykładzie pojawia się w zakresie bloku i nie ma rzeczywistej definicji obiektu int a; Obiektu, program nie łączy połączenia.

Na 6.9.2 Definicje obiektów zewnętrznych , pkt 2 państwa:

Deklaracja identyfikatora obiektu, który ma zakres pliku bez inicjatora i bez specyfikatora klasy przechowywania lub ze specyfikatorem klasy przechowywania static, stanowi wstępna definicja. Jeśli jednostka tłumaczeniowa zawiera jedną lub więcej wstępnych definicji Identyfikator, a jednostka tłumaczeniowa nie zawiera definicji zewnętrznej dla tego identyfikatora, zachowanie jest dokładnie tak, jakby jednostka tłumaczeniowa zawiera deklarację tego zakresu plików Identyfikator, z typem kompozytowym na końcu jednostki tłumaczeniowej, z inicjującym równy 0.

Więc Deklaracja Third Case extern int a; nie kwalifikuje się jako wstępna definicja.

4
Andrew Henle 5 czerwiec 2018, 12:03

przypadek 1:

Deklaracja extern int a ; Deklaracja int a, która jest zdefiniowana w innym miejscu, a tym samym cieniowana zmienna a Deklarowana tutaj: int a = 20 ;. Linker jest następnie poszukuje zmiennej globalnej a i znajdzie go w tym samym pliku z powodu deklaracji {X5}}. Wyjście jest 10.

Case 2:

Tutaj deklaracja extern int a ; nie ma wpływu. Wewnątrz main Zmienna lokalna zadeklarowana tutaj int a = 20 ; jest używana, a tym samym wyjście jest 20.

Case 3:

Jest to podobne do sprawy 1. Skompiluje poprawnie, ale nie łączy się, ponieważ extern int a ; Deklaruje zmienną int a, która jest przypuszczalnie zdefiniowana w innym miejscu, co nie jest przypadkiem, ponieważ skomentowałeś {{x2 }} Deklaracja.

1
Jonathan Leffler 5 czerwiec 2018, 13:55

Case 1: - W pierwszym bloku kodu extern int a; Wskazuje, że zmienna a jest tutaj zadeklarowana, ale mówi Linker, aby znaleźć definicję a powyżej main() powyżej {x3}} nie w main(). Jeśli Linker jest w stanie znaleźć definicję a powyżej main(), wówczas linwo w inny sposób wynika w błąd liniora. W twoim przypadku Linker weźmie a jako 10.

Przypadek 2: - W tym przypadku powyżej zadeklarowany na całym świecie {X0}} mówi do liniora, że definicja a może być w innym pliku lub w tym samym pliku w {{x2} } Funkcja. Tutaj extern int a; mówi, że jeśli go potrzebujesz, będzie definicja A z statyczny czas trwania i wiązanie zewnętrzne ({x4}} zmienna globalna) zdefiniowana w Plik lub w innym pliku . Deklaracja ta jest ukryta przez definicję wewnątrz main(), więc printf() używa local variable. To

printf("A value: %d\n",a);

Rozważ lokalnie zadeklarowany a. Więc drukuje 20.

Case 3: - W tym przypadku oświadczenie

extern int a; // Why now I get a linking error

Powoduje błąd łącznika, ponieważ Linker spróbuje znaleźć definicję a powyżej main() i nie jest tam (skomentowałeś), więc wyniki w Błąd Linkera .

1
Achal 5 czerwiec 2018, 14:06