Frage bezüglich Deklaration und Definition von Variablen und Funktionen



  • Warum geht folgender Code:

    extern int a;
    
    int main()
    {
    a=5;
    }
    
    int a;
    

    Nachfolgender aber nicht:

    int function();
    
    int main()
    {
    function();
    }
    
    int function()
    {
    return 2;
    }
    

    Von dem, was ich mir ergoogelt habe, kann ich folgendes sagen: Der Compiler muss nur wissen, dass a existiert, die Details sind nicht wichtig, da es sich nur um eine Variable handelt. Das heißt ich kann mir die Definition für später sparen, also reicht eine Deklaration.

    In dem zweiten Beispiel muss der Compiler wissen, was sich in function() befindet, weswegen eine Deklaration nicht ausreicht, die Variable muss definiert sein, bevor sie ausgeführt wird.

    Was ich nicht verstehe, ist, warum ich im ersten Beispiel einen Wert für a zuweisen kann, ohne die Variable definiert zu haben. Die Variable a hat doch zu dem Zeitpunkt noch keinen Platz im Speicher reserviert, weil sie ja nur deklariert ist? 😕



  • Ambiguity schrieb:

    Nachfolgender aber nicht:

    int function();
    
    int main()
    {
    function();
    }
    
    int function()
    {
    return 2;
    }
    

    Doch, klar geht das. Wieso meinst du es ginge nicht?



  • Der Compiler weiß zumindest, dass eine Variable a existiert.
    Wo die dann genau liegt, verwaltet u.a auch der Linker.

    Im zweiten BEispiel hast du keine Variable sondern eine Funktion.



  • Deine beiden Beispiele sollten funktionieren. In beiden Fällen wir dem Compiler ein Symbol bekannt gemacht welches später aufgelöst werden. Der einzige kleine Unterschied ist, dass bei Funktionsdeklarationen das extern implizit ist, weshalb es weggelassen werden kann.



  • Ok, danke Leute.
    Hab es jetzt denke ich verstanden. Wenn ich richtig liege, dann wird, sowohl beim Zuweisen der Zahl 5 zur lediglich deklarierten Variable a in meinem ersten Beispiel, als auch beim Aufrufen der lediglich (implizit) deklarierten Funktion function() in meinem zweiten Beispiel, durch den Compiler lediglich eine Referenz / Assoziation zwischen dem Code, in der die Variable / Funktion Verwendung findet, und der Deklaration, hergestellt, welche nach dem Compilieren wiederum eine Referenz / Assoziation auf die Definition der Variable / Funktion aufweist, sodass der Linker nach dem Compilieren beim Zuweisen eines Wertes an die Variable a bzw. beim Aufruf der Funktion function() sofort auf die Definition eben jener verwiesen wird, das heißt für den Linker ist die Deklaration nicht unmittelbar sichtbar, denn darum kümmert sich der Compiler.

    Noch eine Frage: Ist es möglich, eine Funktion explizit zu deklarieren anstatt lediglich implizit, also z.B. so:

    extern int function();
    

    Oder geschieht das lediglich implizit?


  • Mod

    Bist du sicher, ob du weißt, was "explizit" und "implizit" bedeuten? Guck mal in ein Wörterbuch.

    Implizite Funktionsdeklarationen (bei richtiger Verwendung des Wortes "implizit") gibt es in C++ nicht. In C gibt es diese streng genommen noch, aber selbst dort ist das ein Relikt aus alten Tagen, das eher jede Menge Ärger verursacht, aber aus Kompatibilitätsgründen für Uraltcode beibehalten wird, obwohl es niemand mehr benutzt.

    PS: Du hattest vorher noch eine Frage in deinem Beitrag, die du nun wegeditiert hast. Wenn man die verwirrte Verwendung der Wörter "explizit" und "implizit" ignorierte, dann klang die Frage so, als wolltest du wissen, wie du eine Funktion quasi vor dem Linker verstecken könntest. Das kannst du mit dem Schlüsselwort static erreichen. Das funktioniert auch bei globalen Variablen (Achtung: static Variablen in Funktionen haben eine andere Bedeutung. Die Bedeutung von static ist stark kontextabhängig).



  • SeppJ schrieb:

    Bist du sicher, ob du weißt, was "explizit" und "implizit" bedeuten? Guck mal in ein Wörterbuch.

    Implizite Funktionsdeklarationen (bei richtiger Verwendung des Wortes "implizit") gibt es in C++ nicht. In C gibt es diese streng genommen noch, aber selbst dort ist das ein Relikt aus alten Tagen, das eher jede Menge Ärger verursacht, aber aus Kompatibilitätsgründen für Uraltcode beibehalten wird, obwohl es niemand mehr benutzt.

    PS: Du hattest vorher noch eine Frage in deinem Beitrag, die du nun wegeditiert hast. Wenn man die verwirrte Verwendung der Wörter "explizit" und "implizit" ignorierte, dann klang die Frage so, als wolltest du wissen, wie du eine Funktion quasi vor dem Linker verstecken könntest. Das kannst du mit dem Schlüsselwort static erreichen. Das funktioniert auch bei globalen Variablen (Achtung: static Variablen in Funktionen haben eine andere Bedeutung. Die Bedeutung von static ist stark kontextabhängig).

    Danke für die Antwort.

    Unter implizit verstehe ich, dass man ohne das Hinzufügen durch ein Schlüsselwort eine Funktion deklariert, statt sie zu definieren.

    Wenn ich bei

    int function();
    

    die geschweiften Klammern weglasse und stattdessen ein Semikolon einfüge, wäre diese ja nicht definiert, sondern lediglich deklariert. Um das selbe bei Variablen zu erreichen, müsste ich das Schlüsselwort "extern" verwenden. Was ich mich frage, ist, ob es so ein Schlüsselwort auch für Funktionen gibt.

    Bezüglich deines Nachtrags: Meine Annahme war, dass der Linker nichts von der Deklaration mitbekommt und dass der Compiler dafür sorgt, dass an der Stelle der Deklaration die Definition steht. Ob das allerdings stimmt, weiß ich nicht. Ich beherrsche die Grundlagen von Java und setze mich seit gestern das erste Mal mit C++ auseinander, mir gefällt die zusätzliche Kontrolle, aber Konzepte wie der Compiler und Linker sind mir relativ neu, deswegen kann es gut sein, dass ich da was komplett falsch verstehe.

    Danke für die Hilfe 🙂


  • Mod

    Ambiguity schrieb:

    Unter implizit verstehe ich, dass man ohne das Hinzufügen durch ein Schlüsselwort eine Funktion deklariert, statt sie zu definieren.

    Wenn ich bei

    int function();
    

    die geschweiften Klammern weglasse und stattdessen ein Semikolon einfüge, wäre diese ja nicht definiert, sondern lediglich deklariert. Um das selbe bei Variablen zu erreichen, müsste ich das Schlüsselwort "extern" verwenden. Was ich mich frage, ist, ob es so ein Schlüsselwort auch für Funktionen gibt.

    Ja, solch ein Schlüsselwort gibt es: extern ! Darf man auch auf Funktionen anwenden. Macht nur niemand explizit, weil es bereits implizit ist 😉

    Bezüglich deines Nachtrags: Meine Annahme war, dass der Linker nichts von der Deklaration mitbekommt und dass der Compiler dafür sorgt, dass an der Stelle der Deklaration die Definition steht. Ob das allerdings stimmt, weiß ich nicht. Ich beherrsche die Grundlagen von Java und setze mich seit gestern das erste Mal mit C++ auseinander, mir gefällt die zusätzliche Kontrolle, aber Konzepte wie der Compiler und Linker sind mir relativ neu, deswegen kann es gut sein, dass ich da was komplett falsch verstehe.

    Deine Beschreibung passt so ungefähr, nutzt aber teilweise die falschen Worte. Deklarationen und Definitionen sind Dinge auf Ebene des Compilers. Wenn der Linker dran kommt, dann ist davon nicht mehr viel übrig. Wenn der Compiler eine Definition einer Funktion foo übersetzt, dann erzeugt er Code mit der Bemerkung, dass dieser Code zu einem Gebilde Namens foo gehört. Wenn er einen Aufruf einer Funktion foo übersetzt (die vorher deklariert werden musste, damit er das richtig übersetzen kann), dann steht irgendwo in dieser Übersetzung ein Teil der sagt, dass hier der Ort des Gebildes foo eingesetzt werden muss.

    Der Linker bekommt dann später sämtliche Codeteile und die Beschreibungen, wie diese Codeteile heißen und sucht dann nach Stellen, wo bisher nur Verweise stehen. Er guckt dann, ob er diese Verweise auflösen kann und macht dann genau das.

    Das ist nicht unbedingt als Teil der Sprache C++ anzusehen. C++ muss nicht unbedingt mit Compiler und Linker übersetzt werden. Es gibt bloß viele Sprachelemente, die eigentlich nur dazu dienen, diese Art des Übersetzungsprozesses überhaupt erst zu ermöglichen und zu vereinfachen. Denn die Compiler/Linker-Kombination ist einfach eine altbewährte Technik bei der Erstellung von Programmen und viele andere Programmiersprachen werden in ähnlicher Weise übersetzt.



  • Ambiguity schrieb:

    Unter implizit verstehe ich, dass man ohne das Hinzufügen durch ein Schlüsselwort eine Funktion deklariert, statt sie zu definieren.

    "explizit" und "implizit" sind ganz normale Elemente der deutschen Sprache und keine C++-spezifischen Fachbegriffe.

    Ambiguity schrieb:

    Wenn ich bei [...] die geschweiften Klammern weglasse und stattdessen ein Semikolon einfüge, wäre diese ja nicht definiert, sondern lediglich deklariert. Um das selbe bei Variablen zu erreichen, müsste ich das Schlüsselwort "extern" verwenden. Was ich mich frage, ist, ob es so ein Schlüsselwort auch für Funktionen gibt.

    Nein, gibt es nicht, wieso auch, man braucht es ja nicht!? Das Problem mit Variablen ist, dass int a; allein bereits eine Definition und nicht nur eine Deklaration ist.

    Ambiguity schrieb:

    Meine Annahme war, dass der Linker nichts von der Deklaration mitbekommt und dass der Compiler dafür sorgt, dass an der Stelle der Deklaration die Definition steht. Ob das allerdings stimmt, weiß ich nicht.

    Nö, es ist eher genau umgekehrt. Eine Deklaration sagt dem Compiler: Irgendwo gibt es folgendes Ding mit folgendem Namen. Je nachdem, um was es sich handelt, genügt dies, dass der Compiler Maschinencode ausspucken kann, der dieses Ding verwendet. Am Beispiel einer Funktion bzw. Variable: Sobald der Compiler weiß, dass es irgendwo eine Funktion mit Namen "function" mit einer bestimmten Parameterliste und Returntype bzw. einen int namens "a" gibt, kann der Compiler einfach Maschinencode generieren, der diese Dinger verwendet. Anstatt der Adresse der jeweiligen Entitäten, enthält der Maschinencode überall das Symbol "function" bzw. "a". Der Linker geht dann her, baut das Programm aus den Object Files, die aus den einzelnen Source Files erzeugt wurden, zusammen. Der Linker weiß an dieser Stelle, welche Adresse jedes Ding im Programm hat und ersetzt alle Symbole durch die jeweiligen Adressen ("löst das Symbol auf")...



  • SeppJ schrieb:

    Ambiguity schrieb:

    Unter implizit verstehe ich, dass man ohne das Hinzufügen durch ein Schlüsselwort eine Funktion deklariert, statt sie zu definieren.

    Wenn ich bei

    int function();
    

    die geschweiften Klammern weglasse und stattdessen ein Semikolon einfüge, wäre diese ja nicht definiert, sondern lediglich deklariert. Um das selbe bei Variablen zu erreichen, müsste ich das Schlüsselwort "extern" verwenden. Was ich mich frage, ist, ob es so ein Schlüsselwort auch für Funktionen gibt.

    Ja, solch ein Schlüsselwort gibt es: extern ! Darf man auch auf Funktionen anwenden. Macht nur niemand explizit, weil es bereits implizit ist 😉

    Bezüglich deines Nachtrags: Meine Annahme war, dass der Linker nichts von der Deklaration mitbekommt und dass der Compiler dafür sorgt, dass an der Stelle der Deklaration die Definition steht. Ob das allerdings stimmt, weiß ich nicht. Ich beherrsche die Grundlagen von Java und setze mich seit gestern das erste Mal mit C++ auseinander, mir gefällt die zusätzliche Kontrolle, aber Konzepte wie der Compiler und Linker sind mir relativ neu, deswegen kann es gut sein, dass ich da was komplett falsch verstehe.

    Deine Beschreibung passt so ungefähr, nutzt aber teilweise die falschen Worte. Deklarationen und Definitionen sind Dinge auf Ebene des Compilers. Wenn der Linker dran kommt, dann ist davon nicht mehr viel übrig. Wenn der Compiler eine Definition einer Funktion foo übersetzt, dann erzeugt er Code mit der Bemerkung, dass dieser Code zu einem Gebilde Namens foo gehört. Wenn er einen Aufruf einer Funktion foo übersetzt (die vorher deklariert werden musste, damit er das richtig übersetzen kann), dann steht irgendwo in dieser Übersetzung ein Teil der sagt, dass hier der Ort des Gebildes foo eingesetzt werden muss.

    Der Linker bekommt dann später sämtliche Codeteile und die Beschreibungen, wie diese Codeteile heißen und sucht dann nach Stellen, wo bisher nur Verweise stehen. Er guckt dann, ob er diese Verweise auflösen kann und macht dann genau das.

    Das ist nicht unbedingt als Teil der Sprache C++ anzusehen. C++ muss nicht unbedingt mit Compiler und Linker übersetzt werden. Es gibt bloß viele Sprachelemente, die eigentlich nur dazu dienen, diese Art des Übersetzungsprozesses überhaupt erst zu ermöglichen und zu vereinfachen. Denn die Compiler/Linker-Kombination ist einfach eine altbewährte Technik bei der Erstellung von Programmen und viele andere Programmiersprachen werden in ähnlicher Weise übersetzt.

    dot schrieb:

    Ambiguity schrieb:

    Unter implizit verstehe ich, dass man ohne das Hinzufügen durch ein Schlüsselwort eine Funktion deklariert, statt sie zu definieren.

    "explizit" und "implizit" sind ganz normale Elemente der deutschen Sprache und keine C++-spezifischen Fachbegriffe.

    Ambiguity schrieb:

    Wenn ich bei [...] die geschweiften Klammern weglasse und stattdessen ein Semikolon einfüge, wäre diese ja nicht definiert, sondern lediglich deklariert. Um das selbe bei Variablen zu erreichen, müsste ich das Schlüsselwort "extern" verwenden. Was ich mich frage, ist, ob es so ein Schlüsselwort auch für Funktionen gibt.

    Nein, gibt es nicht, wieso auch, man braucht es ja nicht!? Das Problem mit Variablen ist, dass int a; allein bereits eine Definition und nicht nur eine Deklaration ist.

    Ambiguity schrieb:

    Meine Annahme war, dass der Linker nichts von der Deklaration mitbekommt und dass der Compiler dafür sorgt, dass an der Stelle der Deklaration die Definition steht. Ob das allerdings stimmt, weiß ich nicht.

    Nö, es ist eher genau umgekehrt. Eine Deklaration sagt dem Compiler: Irgendwo gibt es folgendes Ding mit folgendem Namen. Je nachdem, um was es sich handelt, genügt dies, dass der Compiler Maschinencode ausspucken kann, der dieses Ding verwendet. Am Beispiel einer Funktion bzw. Variable: Sobald der Compiler weiß, dass es irgendwo eine Funktion mit Namen "function" mit einer bestimmten Parameterliste und Returntype bzw. einen int namens "a" gibt, kann der Compiler einfach Maschinencode generieren, der diese Dinger verwendet. Anstatt der Adresse der jeweiligen Entitäten, enthält der Maschinencode überall das Symbol "function" bzw. "a". Der Linker geht dann her, baut das Programm aus den Object Files, die aus den einzelnen Source Files erzeugt wurden, zusammen. Der Linker weiß an dieser Stelle, welche Adresse jedes Ding im Programm hat und ersetzt alle Symbole durch die jeweiligen Adressen ("löst das Symbol auf")...

    Danke ihr beiden, durch die verschiedenen Erklärungen ist es mir jetzt besonders klar geworden 😃


Log in to reply