Den richtigen Code posten: reduziertes kompilierbares Beispiel



  • Wie man Probleme nachstellbar und nachvollziehbar macht

    Oft findet man hier im Forum Fragen von Neulingen mit Problemen beim Übersetzen ihrer Programme oder zur Laufzeit. Manche liefern dazu garkeinen Code, andere nur spärliche Brocken oder aber hunderte irrelevanter Zeilen, wieder andere posten ungetesteten Pseudocode, der haufenweise Proleme hat, nur nicht das, um welches es geht. Weder mit dem einen noch mit dem anderen Extrem ist uns und euch geholfen, daher soll hier ein kleines Tutorial folgen, wie man den relevanten Code postet - und nur den.

    Wenn der Code nicht kompiliert/linkt

    Es muss zwischen Compilerfehlern und Linkerfehlern unterschieden werden. Erstere haben auf allen aktuellen Compilern eine Angabe der Quelldatei und der Zeile, in der der Fehler auftritt. Letztere haben das nicht immer. Unter MSVC haben Compilerfehler einen Fehlercode Cxxxx und Linkerfehler LNKxxxx, wobei xxxx eine Ziffernkombination ist.
    Grundsätzlich solltest du immer die Fehlermeldung mit angeben, allerdings auch nur eine, nicht einen ganzen Haufen. Nach einem Fehler sind andere Fehlermeldungen häufig Folgefehler, auch wenn man das nicht auf den ersten Blick sieht. Versuche auf jeden Fall, die Meldung zu verstehen, um zu wissen, was zum Fehler gehört und was nicht. Ein paar Brocken Englisch sind (bei englischen Compilern) von Nutzen. Wenn du Wörter aus den Fehlermeldungen nicht verstehst, lass sie dir übersetzen oder google nach den Fachbegriffen - oder der gesamten Fehlermeldung.
    Gib von vornherein an, welchen Compiler und welche IDE du benutzt (nicht verwechseln!), außerdem ist es gut zu wissen, welche Compileroptionen du gewählt hast (wenn du einfach aus der IDE heraus baust ohne was geändert zu haben sag das explizit dazu). Beachte Warnmeldungen und poste sie ggf. auch noch. Die meisten Warnungen weisen auf mögliche Fehlerquellen hin und sollten behoben werden.

    Linkerfehler

    Linkerfehler sind sehr häufig fehlende Funktions- oder Variablendefinitionen. Siehe dazu den FAQ-Eintrag zu undefined references.
    Das Gegenteil dazu ist der Fehler "multiple definition of..." - er erscheint meist, weil eine globale Variable oder eine freie, nicht-inline Funktion in einem Header definiert wurde.

    Linkerfehler beziehen sich meistens auf ein bestimmtes Symbol, d.h. einen Funktionsnamen oder Variablennamen. Jeder Code, der diesen Namen enthält, ist hierbei relevant, d.h. aufrufende Funktionen, die Funktion/Variable selbst, die zugehörige Klasse, wenn es sich um eine Methode handelt. Beim Reduzieren des Codes (s.u.) sollten unbetiligte Funktionen und Variablen komplett entfernt werden. Wenn sich die Fehlermeldung auf eine Funktion bezieht, kann ihre Funktionalität normalerweise auf wenige Zeilen reduziert werden.

    Compilerfehler

    Compilerfehler betreffen normalerweise nur eine Handvoll Variabeln und Funktionen. Für das Nachstellen solcher Fehler wird nur eine .cpp und die (direkt oder indirekt) eingebundenen Header benötigt, alles weitere ist überflüssig. Tritt der Fehler in einer Funktionsdefinition auf, dann ist das die einzige Definition die man braucht, alle anderen Funktionen dürfen beim Reduzieren auf Deklarationen zusammengeschrumpft werden (beteiligte Funktionstemplates ausgeschlossen).

    Ein einzelner Compilerfehler kann bei Templates viele Zeilen umfassen, wenn eine fehlerhafte Benutzung eines Templates an einer Stelle erst viele Instantiierungsstufen tiefer zu einem Fehler führt. Bei Template-Fehlern (z.B: in der Standardbilbiothek) solltest du daher immer sicherstellen, dass du die wesentlichen Teile der kompletten Fehlermeldung einsetzt, nicht nur die erste Zeile. Dazu gehören vor Allem die Zeilen, die deinen eigenen Code betreffen und der eigentliche Fehler.

    Laufzeitfehler

    Laufzeitfehler sind am schwierigsten zu reduzieren, weil hier oft nicht klar ist, in welcher Ecke was schief läuft. Aus dem Grund ist es wichtig, den Fehler zu lokalisieren, dazu ist es unumgänglich, sich mit der Benutzung eines Debuggers vertraut zu machen.
    Wenn das Debuggen nichts hilft, können wir natürlich gerne weiterhelfen. Dazu brauchen wir neben dem Code eine möglichst genaue Beschreibung des Verhaltens, das ihr erwartet und des Verhaltens, das ihr tatsächlich bekommt.

    Den Code reduzieren

    Wenn du hier ins Forum kommst um eine Lösung für das Problem zu finden, heißt das, dass du schon einige Zeit suchst (bei Google, in deinem Code, im Buch deiner Wahl...). Um hier zügig eine Antwort zu bekommen investierst du am besten noch ein kleines bisschen mehr Zeit in die Kürzung und Formatierung deines Codes, und das geht so:

    • Sichere deinen Code und lege eine Kopie davon an. (z.B. einen neuen branch in deiner Versinskontrolle) Von jetzt an arbeitest du auf der Kopie.
    • Lösche großzügig die Teile raus, von denen du sicher bist, dass sie zum Problem nichts beitragen. (Funktionen, Klassen, #includes, ...)
    • Übersetze den Code und stell sicher, dass du genau die selben Fehler hast wie vorher, nicht mehr und nicht weniger.
    • Ersetze komplizierte Funktionalität Schrittweise durch was einfacheres, z.B. Funktionsaufrufe durch dummy-Werte usw. Auf die Weise reduzierst du die Abhängigkeiten deines Codes von weiteren Headern usw., die du dadurch auch rauswerfen kannst.
    • Übersetze nach jedem Schritt und stell sicher, dass du genau die selben Fehler hast wie vorher, nicht mehr und nicht weniger.
    • Verkleinere deine Klassen: entferne Membervariablen und Methoden, die den Fehler nicht beeinflussen.
    • Übersetze...
    • Ersetze #includes von eigenen Headern durch den Inhalt der Header, so dass du weniger Dateien bekommst.
    • Wenn du in einem der Schritte neue Fehler bekommst, behebe sie oder mache den Schritt rückgängig.
    • Wenn du in einem der Schritte deinen Fehler nicht mehr bekommst, denk drüber nach, ob dir das einen Hinweis gibt, woher der Fehler kommen könnte. Mache den Schritt rückgängig (Versionskontrolle hilft!) und fahre mit kleineren Schritten fort, den Fehler einzukreisen.

    Du kommst zu einem Punkt, in dem du den Code nicht mehr weiter reduzieren kannst. Typischerweise hast du ca. 20-60 Zeilen, verteilt auf 1-5 Dateien.
    Diesen Code kopierst und postest (nicht Abtippen!) du jetzt komplett zu deiner Fragestellung (und Fehlermeldung, Compiler, -optionen...):

    • Lasse nichts weg, damit wir den Code einfach nur zu kopieren brauchen um den Fehler nachstellen zu können.
    • Benutze das [code="cpp"][/code]-Tag (Button "C++" unter den Smileys)
    • Rücke deinen Code ordentlich ein (hast du ja sicherlich sowieso, oder?)
    • Schreibe mit Kommentaren die Namen der einzelnen Sourcen und Header vor die Codeabschnitte
    • Trenne den Code verschiedener Dateien sichtbar voneinander - entweder durch mehrere Leerzeilen oder indem du sie jeweils in einen eigenen [cpp]-Tag setzt.
    • Idealerweise postest du den so reduzierten Code direkt bei ideone.com oder einem anderen online-compiler und lieferst zusätzlich zu dem hier geposteten Code einen Link dorthin.

    Zu viel Aufwand? Du möchtest, dass sich zig Leute hier mehr als nur einen Augenblick mit deinem Problem beschäftigen. Also solltest du auch bereit sein, dafür entsprechend viel Zeit zu investieren. Je mehr Mühe du dir gibst, desto leichter machst du es anderen, dir zu helfen, und desto wahrscheinlicher ist es, dass jemand sich auch wirklich mit deinem Problem beschäftigt.

    ~
    Edit durch Arcoth: C++-Tag korrigiert.~


Anmelden zum Antworten