Anfängerfrage Pointer Deklaration



  • Sorry, da habe ich mich wohl schlecht ausgedrückt.
    Ich wollte eigentlich sagen, dass ich nicht weiß was rauskommt wenn der Preprozessor das File CDynPointArray.h bearbeitet.
    Das haben wir bisher auch noch nicht behandelt in der Vorlesung.



  • Wenn ein File kompiliert wird, dann ersetzt der Preprozessor alle Zeilen ala

    #include "File.h"

    mit dem Inhalt des Files File.h .
    Nach der Ersetzung wird das Ergebnis vom Compiler übersetzt. Ganz so als ob das Ergebnis der Ersetzung direkt so in einem File stehen würde.

    Jetzt überleg dir was beim File CDynPointArray.h nach dem Ersetzen der #include "CCleanRobot.h" Zeile rauskommt, und ob das so kompilieren kann.
    Dann solltest du sehen was der Fehler ist, und wie man ihn vermeiden kann.

    (Bzw. eine Sache gibt es, die du vielleicht noch nicht kennst, die man manchmal braucht um solche Fehler zu vermeiden. Und die nennt sich Forward-Declaration. Kannst du gerne googeln und dich dazu schlaulesen. In diesem speziellen Fall brauchst du das aber nichtmal.)



  • Hmm ich habe jetzt mal die Zeile:
    #include "CCleanRobot.h"
    rausgeschmissen und jetzt kann ich Objekte vom Typ CDynPointArray in der CCleanRobot.h ohne Fehlermeldung erstellen, nur der Grund wird mir noch nicht so klar, ich denke die nächsten Tage da nochmal genauer darüber nach und frage dann bei Bedarf einfach nochmal nach.
    Danke ebenfalls für den Tipp sich mal Forward-Declaration anzusehen, vielleicht hilft es mir auch um das zu verstehen.



  • killbill schrieb:

    Hmm ich habe jetzt mal die Zeile:
    #include "CCleanRobot.h"
    rausgeschmissen und jetzt kann ich Objekte vom Typ CDynPointArray in der CCleanRobot.h ohne Fehlermeldung erstellen,

    War mir klar. Hab ich dir nicht geschrieben eben weil...

    killbill schrieb:

    nur der Grund wird mir noch nicht so klar, ich denke die nächsten Tage da nochmal genauer darüber nach und frage dann bei Bedarf einfach nochmal nach.

    Deswegen hab ich geschrieben du sollst dir überlegen was der Preprozessor hier macht.
    Du kannst auch einfach mal hergehen und den ganzen Inhalt von CCleanRobot.h an die Stelle kopieren wo du das #include "CCleanRobot.h" stehen hattest. Dann nochmal übersetzen und gucken wo der Fehler gemeldet wird. Vielleicht siehst du es dann. Bzw. wenn nicht kannst du ja nochmal nachfragen.

    killbill schrieb:

    Danke ebenfalls für den Tipp sich mal Forward-Declaration anzusehen, vielleicht hilft es mir auch um das zu verstehen.

    Hat mit dem Fall hier wie gesagt nix zu tun. Forward-Declarations brauchst du nur, wenn du die #include Zeile eben nicht einfach ersatzlos entfernen kannst.


  • Mod

    Gehen wir mal Schritt für Schritt durch den Code, was der Präprozessor daraus macht. Denk dran, was hustbaer erklärt hat:

    hustbaer schrieb:

    Wenn ein File kompiliert wird, dann ersetzt der Preprozessor alle Zeilen ala

    #include "File.h"

    mit dem Inhalt des Files File.h .
    Nach der Ersetzung wird das Ergebnis vom Compiler übersetzt. Ganz so als ob das Ergebnis der Ersetzung direkt so in einem File stehen würde.

    Dein Code ganz zu Anfang:

    #define _USE_MATH_DEFINES
    #include "CCleanRobot.h"
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Merke: Ich betrachte hier nur den Präprozessor, also nur Zeilen, die mit '#' beginnen. Der Compiler läuft üerhaupt erst an, nachdem der Präprozessor fertig ist. Der Präprozessor geht einfach von oben nach unten durch den Code und macht reine Textersetzungen. Er versteht überhaupt gar nichts von der Sprache C++. Der Code ist für ihn bloß Text, außer eine Zeile beginnt mit '#'. Daher ist der erste Schritt, an dem er aktiv wird die Zeile #define _USE_MATH_DEFINES . Also: Erster Schritt: Setze das Makro _USE_MATH_DEFINES. Zweiter Schritt: Setze den Inhalt von "CCleanRobot.h" ein. dann steht da noch:

    #ifndef CCLEANROBOT_H_
    #define CCLEANROBOT_H_
    
    #define _USE_MATH_DEFINES
    #include <cmath>
    #include "cleanerbot/World.h"
    #include "CPoint.h"
    #include "CDynPointArray.h"
    
    // Definition der Klasse CCleanRobot
    
    #endif /* CCLEANROBOT_H_ */
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Dieser Code wird weiter verarbeitet: #ifndef CCLEANROBOT_H_ ist wahr (CCLEANROBOT_H_ ist bisher nicht gesetzt), also wird alles bis zum #endif auch gemacht. Der erste Schritt ist das Setzen von CCLEANROBOT_H_. Dann wird wieder _USE_MATH_DEFINES gesetzt. Dann wird cmath eingefügt und verarbeitet. Dann wird "cleanerbot/World.h" eingesetzt (die du uns noch nicht gezeigt hast) und verarbeitet. Dann wird "CPoint.h" eingesetzt:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    #ifndef CPOINT_H_
    #define CPOINT_H_
    
    // Definition der Klasse CPoint
    
    #endif /* CPOINT_H_ */
    #include "CDynPointArray.h"
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Das wird auch weiter verarbeitet. CPOINT_H_ ist noch nicht gesetzt, daher wird der Inhalt des ifndef-Block ausgewertet, was unter anderem bedeutet, dass CPOINT_H_ nun gesetzt ist. Dann folgt #include "CDynPointArray.h" , dessen Inhalt nun eingesetzt wird:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    #ifndef CDYNPOINTARRAY_H_
    #define CDYNPOINTARRAY_H_
    
    #include "CPoint.h"
    #include "CCleanRobot.h"
    
    // Definition der Klasse CDynPointArray
    
    #endif /* CDYNPOINTARRAY_H_ */
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    CDYNPOINTARRAY_H_ ist noch nicht gesetzt, der ifndef-Block wird verarbeitet. CDYNPOINTARRAY_H_ wird gesetzt. DEr Inhalt von "CPoint.h" wird eingesetzt:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    #ifndef CPOINT_H_
    #define CPOINT_H_
    
    // Definition der Klasse CPoint
    
    #endif /* CPOINT_H_ */
    #include "CCleanRobot.h"
    
    // Definition der Klasse CDynPointArray
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Jetzt wird klar, wozu die Includeguards wirklich da sind: CPOINT_H_ wurde oben schon gesetzt, das heißt, der ifndef-Block wird entfernt:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    #include "CCleanRobot.h"
    
    // Definition der Klasse CDynPointArray
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Das ist auch gut so, denn sonst hätten wir eine doppelte Definition der Klasse CPoint, was nicht erlaubt ist. Nun wird "CCleanRobot.h" eingesetzt:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    #ifndef CCLEANROBOT_H_
    #define CCLEANROBOT_H_
    
    #define _USE_MATH_DEFINES
    #include <cmath>
    #include "cleanerbot/World.h"
    #include "CPoint.h"
    #include "CDynPointArray.h"
    
    // Definition der Klasse CCleanRobot
    
    #endif /* CCLEANROBOT_H_ */
    // Definition der Klasse CDynPointArray
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    CCLEANROBOT_H_ wurde oben bereits gesetzt. Also steht da aus Sicht des Präprozessors:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    // Definition der Klasse CDynPointArray
    
    // Definition der Klasse CCleanRobot
    
    #include <iostream>
    #include <cmath>
    
    // Definition der CCleanRobot-Methoden
    

    Und zu guter Letzt, nachdem der Präprozessor fertig ist, bleibt für den Compiler folgender Code:

    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition der Klasse CPoint
    
    // Definition der Klasse CDynPointArray
    
    // Definition der Klasse CCleanRobot
    
    // Inhalt von iostream
    // Inhalt von cmath
    
    // Definition der CCleanRobot-Methoden
    

    Dieser Code wird an den Compiler übergeben. Und wir sehen: In diesem Fall passt alles! 😕 😮
    Folgerung: Entweder tritt der Fehler nicht bei der Verarbeitung von CCleanRobot.cpp auf (du hast nie gesagt, bei welcher Datei der Fehler auftritt, es war bloß die einzige, die du gezeigt hast) oder der Inhalt von "cleanerbot/World.h" ist doch wichtig oder der Code ist nicht 1:1 der Fehlercode.

    Damit das Beispiel nicht ganz umsonst ist, rate ich mal den Inhalt deiner CDynPointArray.cpp, von der ich annehme, dass bei ihrer Verarbeitung der Fehler auftritt:

    #include "CDynPointArray.h"
    
    // Definition der CDynPointArray-Methoden
    

    Nun machen wir das Spielchen wieder und setzen zunächst CDynPointArray.h ein:

    #ifndef CDYNPOINTARRAY_H_
    #define CDYNPOINTARRAY_H_
    
    #include "CPoint.h"
    #include "CCleanRobot.h"
    
    // Definition von CDynPointArray
    
    #endif /* CDYNPOINTARRAY_H_ */
    
    // Definition der CDynPointArray-Methoden
    

    CDYNPOINTARRAY_H_ ist nicht gesetzt, also wird der Block ausgewertet. Dadurch wird CDYNPOINTARRAY_H_ gesetzt und CPoint wird eingesetzt und verarbeitet, es bleibt:

    // Definition von CPoint
    
    #include "CCleanRobot.h"
    
    // Definition von CDynPointArray
    
    // Definition der CDynPointArray-Methoden
    

    "CCleanRobot.h" wird eingesetzt:

    // Definition von CPoint
    
    #ifndef CCLEANROBOT_H_
    #define CCLEANROBOT_H_
    
    #define _USE_MATH_DEFINES
    #include <cmath>
    #include "cleanerbot/World.h"
    #include "CPoint.h"
    #include "CDynPointArray.h"
    
    // Definition von CCleanRobot
    
    #endif /* CCLEANROBOT_H_ */
    
    // Definition von CDynPointArray
    
    // Definition der CDynPointArray-Methoden
    

    CCLEANROBOT_H_ ist noch nicht gesetzt, der Block wird ausgewertet. CCLEANROBOT_H_ wird gesetzt, _USE_MATH_DEFINES wird gesetzt, cmath wird eingesetzt und verarbeitet, cleanerbot/World.h wird eingesetzt und verarbeitet (nichts bleibt, da CPOINT_H_ bereits gesetzt wurde):

    // Definition von CPoint
    
    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    #include "CDynPointArray.h"
    
    // Definition von CCleanRobot
    
    // Definition von CDynPointArray
    
    // Definition der CDynPointArray-Methoden
    

    Nun wird noch "CDynPointArray.h" eingesetzt und jetzt geht's schief:

    // Definition von CPoint
    
    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    #ifndef CDYNPOINTARRAY_H_
    #define CDYNPOINTARRAY_H_
    
    #include "CPoint.h"
    #include "CCleanRobot.h"
    
    // Definition von CDynPointArray
    
    #endif /* CDYNPOINTARRAY_H_ */
    
    // Definition von CCleanRobot
    
    // Definition von CDynPointArray
    
    // Definition der CDynPointArray-Methoden
    

    Upps! CDYNPOINTARRAY_H_ wurde bereits gesetzt! Was zwischen dem ifndef und dem endif steht wird daher gelöscht:

    // Definition von CPoint
    
    // Inhalt von cmath
    // Inhalt von "cleanerbot/World.h"
    
    // Definition von CCleanRobot
    
    // Definition von CDynPointArray
    
    // Definition der CDynPointArray-Methoden
    

    Dieser Code wird an den Compiler übergeben. Doch in der Definition von CCleanRobot kommt der Typ CDynPointArray vor. Der Compiler geht auch von oben nach unten durch den Code. Das heißt, zu der Zeit, wo er die Definition von CCleanRobot verarbeitet, kennt er den Typ CDynPointArray noch nicht, da dieser erst später definiert wird. ➡ Fehler!
    Und daher schützen dich Includeguards nur vor doppelten Definitionen, aber nicht davor, dass durch unnötige Zirkelschlüsse die Reihenfolge von Definitionen auf einmal nicht mehr stimmt. Das #include "CCleanRobot.h" in CDynPointArray.h war schließlich absolut überflüssig, da CDynPointArray nicht von CCleanRobot abhängt.



  • Woah, danke für das ausführliche Beispiel SeppJ.
    Ich habe eben mal versucht das ganze nachzuvollziehen, aber ich glaube es ist einfach zu spät gerade, ich hole das aber auf jeden Fall die nächsten Tage mal nach, falls dann noch Fragen sind melde ich mich einfach wieder.
    Ebenfalls auch nochmal danke an dieser Stelle an hustbaer.

    Edit:
    @SeppJ
    Du hast die cpp Datei von CDynPointArray fast richtig erraten, sie lautet:
    #include "CDynPointArray.h"
    #include <iostream>
    wobei das #include <iostream> eigentlich überflüssig ist, da ich es ja schon aus der h Datei übernehme.
    Der Fehler ist übrigens immer in der CCleanRobot.h aufgetreten, wenn ich ein Attribut vom Typ CDynPointArray in der CCleanRobot.h Datei deklariert habe, ich dachte eigentlich ich habe das mal erwähnt.


  • Mod

    In 10 Minuten wirst du das als Anfänger auch kaum nachvollziehen können. Nimm dir Zeit!



  • 👍
    Wow, das ist mal ne Antwort, in der viel Arbeit steckt!


  • Mod

    Jockelx schrieb:

    👍
    Wow, das ist mal ne Antwort, in der viel Arbeit steckt!

    Copy&Paste FTW!



  • die Arbeit kann man sich erleichtern, indem man sich einfach ansieht, wie der Quelltext-Strom nach dem Präpro und vor dem eigentlichen Compilieren aussieht: gcc -E



  • @großbuchstaben
    Ist da noch nachvollziehbar woher die einzelnen Teile stammen?



  • hustbaer schrieb:

    @großbuchstaben
    Ist da noch nachvollziehbar woher die einzelnen Teile stammen?

    Ja.

    # 1 "main.cpp"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 1 "<command-line>" 2
    # 1 "main.cpp"
    # 1 "foo.h" 1
    struct foo
    {
        int func();
    };
    # 2 "main.cpp" 2
    # 1 "bar.h" 1
    struct bar
    {
        char func();
    };
    # 3 "main.cpp" 2
    
    int main()
    {
    
    }
    


  • Cool.
    Zwar nicht unbedingt schön zu lesen, aber wenn man sich mal dran gewöhnt hat geht das vermutlich ganz gut.


Anmelden zum Antworten