Global, Modulglobal, Lokal



  • freakC++ schrieb:

    Angenommen ich habe eine .cpp Datei in der ich eine globale Variable deklariere:

    #include <iostream>
    ...
    int global;
    ...
    

    dann ist diese Variable ja nur in dieser Unit bekannt (soweit ich keine anderen eingebunden habe).

    Hier definierst Du eine Variable mit externer Bindung. Das heißt, es wird Speicher reserviert. Wenn Du in einer zweiten Übersetzungseinheit (ÜE) einer neuen Varialben denselben Namen gibst und die beiden ÜEs verlinkst, ist das eine Verletzung der "Eine-Definition-Regel" --> Der Linker wird sich beschweren, dass "global" zweimal definiert worden ist. Obwohl diese Variable vielleicht nicht anderen ÜEs "bekannt" ist, wird der "Name verbraucht", weil die Variable eine externe Bindung besitzt.

    Gibst Du in anderen ÜEs nur folgendes an:

    extern int global;
    

    deklarierst Du nur eine Variable mit externer Bindung. Du sagst dem Compiler also, dass es eine Variable namens "global" mit externer Bindung gibt, die in irgendeiner anderen ÜE definiert wird (bzw werden sollte).

    Wenn Du Variablen mit interner Bindung haben möchtest, gibt es mehrere Möglichkeiten:

    const int foo1 = 23; // Konstanten haben automatisch interne Bindung
    
    static int foo2;     // "static" bedeutet in diesem Kontext "interne Bindung",
                         // ist also das Gegenteil von "extern" in diesem Fall
    namespace {
      int foo3;          // foo3 im anonymen Namensraum hat auch "interne Bindung"
    }
    

    Der Unterschied zwischen interner und externer Bindung ist der, dass Objekte und Funktionen mit interner Bindung nicht über ihre Namen in anderen ÜEs "sichtbar" sind -- auch dann nicht, wenn man in den anderen ÜEs entsprechende Deklarierungen benutzt. Namen von Objekten und Funktionen mit externer Bindung beziehen sich immer auf dasselbe Objekt bzw dieselbe Funktion. Es sollte i.d.R. genau eine Definition geben. Ausnahmen gibt es aber auch: Klassendefinitionen, Inline-Funktionen und Funktions-Templates. Siehe "one definition rule" in jedem guten C++ Buch.

    Konstanten mit externer Bindung gibt es übrigens auch:
    datei1.cpp:

    extern const int dings = 42;  // Definition
    

    datei2.cpp:

    extern const int dings;       // Deklaration
    

    Hier bezieht sich der Name "dings" wieder auf ein und dieselbe Variable (da externe Bindung).

    Ich hoffe, das hat etwas geholfen. Ich habe da auch erst nicht durchgeblickt. Wichtinge Konzepte sind:

    • Übersetzungseinheit (engl: "translation unit" -- bei #include "soundso" passiert wirklich nur eine einfache Textersetzung beim Preprocessor! All das, was der Preprocessor für ein *.cpp ausspuckt ist quasi die "Übersetzungseinheit")
    • externe/interne Bindung (engl: "external/internal linkage")

    Gruß,
    SP



  • Hallo,
    vielen Dank für den langen Post, Sebastian Pizer. Du hast mir dabei sehr geholfen. Nun zweifel ich jedoch, dass ich den Unterschied
    zwischen Deklaration und Definiton begriffen habe. Ich dachte, dass eine Deklaration die "Bekanntmachung" der Variablen ist und
    eine Definition die erste Wertzuweisung ist.

    Beispiel

    int zahl; //Deklaration
    int nummer = 4; //Definition
    

    Liege ich da falsch? Sonst verstehe ich nicht, warum "int global" eine Definition sein soll.

    Vielen Dank für eure Hilfe
    lg, freakC++



  • freakC++ schrieb:

    Ich dachte, dass eine Deklaration die "Bekanntmachung" der Variablen ist

    nicht nur Variablen, auch Klassen, Funktionen, Klassen-Templates und Funktions-Templates.

    freakC++ schrieb:

    und eine Definition die erste Wertzuweisung ist.

    Nein. Das verwechselst Du mit Initialisierung. Was genau eine Definition bedeutet, hängt davon ab, was definiert wird. Bei Variablen auf Namensraumebene führt eine eine Definition dazu, dass der Compiler für diese Variable Speicher reserviert. Und das sollte bei einer Variablen oder "normalen" Funktion mit externer Bindung in nur genau einer ÜE passieren. ("normal" = kein Inline und kein Template)

    class Klasse;    // Deklaration von Klasse,
                     // Klasse ist nun ein "unvollständiger Typ"
    
    class Klasse {}; // Definition von Klasse,
                     // Klasse ist nun ein "vollständiger Typ"
    
    int foo(int x);              // Deklaration von foo, externe Bindung
    int foo(int x) {return x*2;} // Definition  von foo, externe Bindung
    
    extern int i;           // Deklaration, externe Bindung
    extern const int w;     // Deklaration, externe Bindung
    
    int j;                  // Definition,  externe Bindung
    int k = 99;             // Definition,  externe Bindung
    extern const int p = 2; // Definition,  externe Bindung
    
    const int m = 2;        // Definition,  interne Bindung
    static int t;           // Definition,  interne Bindung
    static int t = 23;      // Definition,  interne Bindung
    

    Ein gutes C++ Buch sollte das alles (ÜEs, Bindung, Deklaration vs Definition) erklären können.

    Gruß,
    SP



  • Hallo,
    vielen Dank auch für deine letute Antwort. Ich denke, dass ich fast alles verstanden habe. Eine Frage bleibt mir noch.

    Du sagst:

    int j; // Definition, externe Bindung

    Ich frage mich, warum die Variable hier eine externe Bindung hat. Was ist dann der Unterschied zu:

    extern int j;

    ?

    Vielen Dank für dein (euer) Bemühen!
    lg, freakC++



  • freakC++ schrieb:

    Hallo,
    vielen Dank auch für deine letute Antwort. Ich denke, dass ich fast alles verstanden habe. Eine Frage bleibt mir noch.

    Du sagst:

    int j; // Definition, externe Bindung

    Ich frage mich, warum die Variable hier eine externe Bindung hat. Was ist dann der Unterschied zu:

    extern int j;

    ?

    Vielen Dank für dein (euer) Bemühen!
    lg, freakC++

    Das erste definiert j in dieser TU und stellt Speicher bereit. j ist für andere TUs sichtbar (deswegen "externe Bindung").

    Das zweite deklariert j (kein Speicher wird hier bereit gestellt) und verweist auf eine andere Variable (meinetwegen auf das erste j).

    Stefan.



  • freakC++ schrieb:

    Du sagst:

    int j; // Definition, externe Bindung

    Ich frage mich, warum die Variable hier eine externe Bindung hat. Was ist dann der Unterschied zu:

    extern int j;

    ?

    Das extern macht aus dem Zweiten eine Deklaration.

    Beschwer Dich bei den Erfindern von C. 🙂

    Dass

    const int x = 99;
    

    per Default interne Bindung hat ist auch eine nette C++ Eigenheit. In C ist das, soweit ich weiß, anders.



  • Hallo,
    nur so eine Frage nebenbei:

    Ich nutzt Visual Studio 2005. Nun erstelle ich zwei Units und speicher sie im selben Verzeichnis. Nun schreibe ich folgendes:

    Unit1.cpp

    #include <iostream>
    
    extern a;
    
    a=4;
    

    Unit2.cpp

    //Ich arbeite eigentlich mit Borland. Ich muss jetzt hier eigentlich Unit1 einbinden, doch es kommt eine Fehlermeldung, dass diese nicht bekannt sei.
    //#include <Unit1.cpp> //funktionert nicht
    //#include "PFAD/Unit1.cpp"
    
    int main()
    {
     cout << a;
    }
    

    Zwar ist mir das Konzept von extern und Co. jetzt klar, doch weiß ich noch nicht, wie ich diese anzuwenden habe. So ist a nicht
    bekannt, obwohl ich ihn ja als extern gekennzeichnet habe.

    Könnt Ihr mal das Beispiel korrigieren oder ein anderes posten, damit ich mir vostellen kann, wie die Benutzung praktisch aussieht?

    Vielen Dank
    lg, freakC++



  • ---- Unit1.cpp
    
    int a = 4;
    
    ---- Unit2.cpp
    
    #include <iostream>
    
    extern int a;
    
    int main() {
    
       std::cout << a << std::endl;
       return 0;
    }
    

    Nur auf die Schnelle, also ungetestet (aber was soll da schon schiefgehen 😉 )

    Stefan.



  • ok, funzt!

    Vielen Dank nochmal!

    lg, freakC++


  • Mod

    Du hast extern falsch verstanden. Ein Beispiel:

    function.cpp

    extern int a;
    
    void function(){
     a=2;
    }
    

    function.h

    void unit1();
    

    a.cpp

    int a;
    

    main.cpp

    #include"function.h"
    #include<iostream>
    
    extern int a;
    
    int main(){
     a=1;
     std::cout<<a; // 1
     function();
     std::cout<<a; // 2
    }
    

    edit: Da war DStefan wohl schneller 😞

    P.S.: Die Gründe die gegen globale Variablen sprechen gelten natürlich noch immer, also gewöhn dich nicht zu sehr daran. Wenn man die Variable auch noch über mehrere Übersetzungseinheiten verteilt, wird es noch schwerer Änderungen nachzuvollziehen.



  • und woher "weiß" externjetzt, auf welche Variable (auf welches a) es verweist?

    Vielen Dank´
    lg, freakC++


  • Mod

    freakC++ schrieb:

    und woher "weiß" externjetzt, auf welche Variable (auf welches a) es verweist?

    Vielen Dank´
    lg, freakC++

    Probier doch mal aus was bei mehreren Definitionen passiert...



  • @freak: Ganz am Anfang hast Du von "Sichtbarkeit" gesprochen und da hätte man eigentlich nen Fragenzeichen druntersetzen müssen. Wenn in einer ÜE etwas mit externer Bindung definiert wird, ist das dem Compiler, wenn er eine andere ÜE übersetzen will, nicht ohne weiteres bekannt. Dafür gibt es ja die Deklarationen, die man üblicherweise in Header-Dateien reinschreibt.



  • freakC++ schrieb:

    und woher "weiß" externjetzt, auf welche Variable (auf welches a) es verweist?

    Vielen Dank´
    lg, freakC++

    Der Compiler weiß das gar nicht (wenn er Unit2.cpp übersetzt). Er erstellt vielmehr eine "externe Referenz", die dann der Linker auflöst. Aus diesem Grund übersetzt der Compiler TUs mit "extern"-Verweisen auch dann klaglos, wenn die referenzierte Variable nirgens definiert wird. Erst der Linker mach in einem solchen Fall Rabatz.

    Der ganze Zauber daran ist ja erst die Überwindung der Grenzen von Translation Units.

    Stefan.



  • freakC++ schrieb:

    und woher "weiß" extern jetzt, auf welche Variable (auf welches a) es verweist?

    Klammere Dich nicht an einzelne Schlüsselwörter. Die Konzepte (Bindung, Decl vs Def) dahinter sind wichtig. Da gibt es leider keine 1:1 Beziehung. Mit extern kannst Du eine externe Bindung erzwingen und sogar u.U. aus einer Definition eine Deklaration machen. Vieles hat aber auch ohne das Schlüsselwort schon eine externe Bindung.

    Bzgl "welches a": Es kann nur ein a mit externer Bindung geben. Definierst Du zwei, schimpft der Linker "multiple definition of ..." (oder so ähnlich). Deklarierst Du nur und definierst nie, schimpft der Linker "undefined reference to ..." (oder so ähnlich). Der Linker ist der, der die ÜEs zusammenknotet. Er schaut sich die Kompilate der ÜEs an. In einem steht vllcht. "Ich habe ein A" (in Form einer Symboltabelle oder so). In einem anderen steht vielleicht "ich brauche ein A". Namen für Dinge mit interner Bindung interessieren den Linker weniger.

    Gruß,
    SP



  • Hallo,
    achso...ähmm noch eine Sache zum zweiten Post von SeppJ. Du schreibst, dass es eben nicht geht, wenn ich die Unit inkludiere.
    Ich habe jetzt mal mit Borland Builder 6 zwei Units erstellt und in einer eine gloable Variable erstellt. Wenn ich jetzt die eine in die
    andere inkludiere, dann ist die Variable bekannt.

    Warum soll das dann deiner Ansicht nicht funktionieren? Habe ich dich da falsch verstanden?

    Vielen Dank
    lg, freakC++



  • Beim #include führt der Preprocessor nur eine einfache Textersetzung durch -- wie schon mehrfach erwähnt. Dem eigentlichen Compiler ist es egal, aus welchen Dateien die ÜE (Übersetzungseinheit) zusammengesetzt wird.

    Schreibst Du

    //---- dings1.cpp ----
    
    int i; // Definition
    
    //---- dings2.cpp ----
    
    #include "dings1.cpp"
    
    int main()
    {
       return i;
    }
    

    hast Du folgende ÜEs:

    //----- ÜE1 (dings1.cpp) -----
    
    int i;
    
    //----- ÜE2 (dings1.cpp in dings2.cpp eingefügt) -----
    
    int i;
    
    int main()
    {
      return i;
    }
    

    Hier enthält jede ÜE eine Definition von i --> Die ÜEs kannst Du also nicht linken (multiple definition of 'i').

    Vielleicht recherchierst Du nochmal nach Preprocessor, Compiler und Linker. Vielleicht liest Du Dir auch nochmal den kompletten Thread durch. Einiges scheint Dir entgangen zu sein. Vielleicht besorgst Du Dir auch mal ein gutes Buch. Wenn Du Dein Alter und/oder Erfahrungsstand verrätst, kann man Dir vielleicht nen guten Buchtipp geben.

    Das, was Du vielleicht willst, sieht so aus:

    //---- dings.hpp -----
    
    #ifndef MEINE_DINGS_KOPFDATEI_GUARD
    #define MEINE_DINGS_KOPFDATEI_GUARD
    
    extern int i; // Deklaration
    
    #endif // Include-Guard endet hier
    
    //---- dings.cpp -----
    
    #include "dings.hpp" // nicht notwendig aber üblich und löblich
    
    //double i = 99; // würde wegen der Deklaration in dings.hpp
                     // zu einem Fehler führen
    
    int i = 99; // Definition von i ist konsistent zur Deklaration
    
    //---- haupt.cpp -----
    
    #include <iostream>
    #include "dings.hpp"
    
    int main() {
      std::cout << i << '\n'; // i wegen der Deklaration in dings.h bekannt.
    }
    

    Gruß,
    SP



  • ich hab neulich auch ne version gesehen, wo man die *.cpp nicht brauchte, ging so in etwa:

    #ifdef GUARD
    #define EXTERN extern
    #endif
    
    #define GUARD
    EXTERN int i;
    
    #undef EXTERN
    

    habs mir aber nicht genau angeguckt, weil ichs zu unübersichtlich fand...

    bb



  • unskilled schrieb:

    ich hab neulich auch ne version gesehen, wo man die *.cpp nicht brauchte, ging so in etwa:

    #ifdef GUARD
    #define EXTERN extern
    #endif
    
    #define GUARD
    EXTERN int i;
    
    #undef EXTERN
    

    habs mir aber nicht genau angeguckt, weil ichs zu unübersichtlich fand...

    bb

    Das klappt nicht.
    Das kann nur absichern, daß die Definition nur einmal pro Übersetzungseinheit da ist. Dazu würde es völlig reichen, sie normal in den Header zu schreiben, dank include guards.

    Was klappt, ist ne andere Version:

    #ifdef VON_DER_MAIN_INLUDIERT
    int i;
    #else
    extern int i;
    #endif
    

    Sowas habe ich sogar mal geschrieben. Ich rate davon ab.



  • Persönlich finde ich, dass nur ein fehlgeschlagenes design extern benötigt, ich war noch nie in der situation wo ich es brauchte. Kann mir mal jemand ein beispiel geben, wo man so etwas brauchen könnte?

    Gruß


Anmelden zum Antworten