[I] Artikel C/C++ - Buildvorgang, allgemein



  • Hallo,
    Ich denke seit einer Weile über einen Artikel nach, und bin jetzt so weit, daß man die Idee diskutieren kann.

    Ich möchte den prinzipiellen Build-Vorgang generell beschreiben und mit kleinen praktischen Beispielen untermauern - erstens als notwendige Vorraussetzung um z.B. einen "IDE-freien" Build einzurichten, aber auch um mit vielen - erstmal unverständlichen - Warnungen und Fehlermeldungen besser umgehen zu können.

    Der Artikel richtet sich also hauptsächlich an "Einsteiger mit Ambitionen".
    Entsprechende Fragen kommen immer mal wieder im Forum und woanders hoch und mir fehlt jedesmal ein schöner Artikel, der das brauchbar erklärt.

    Da mir außer der Zielgruppe einige Dinge noch nicht klar sind, hier mal der Entwurf für das erste Kapitel:

    ________________________________________________________________________

    1. Wie entsteht aus Quellcode ein Programm?

    1.1 Übersetzungsschritte
    Betrachten wir ein Beispielprojekt mit den Dateien mice.h, mice.cpp, men.h, men.cpp:
    Das Übersetzen läßt sich grob in folgende Schritte gliedern:
    Der Präprozessor bearbeitet mice.cpp: Kommentare und überflüssige Leerzeichen erden entfernt und Präprozessor-Anweisungen wie #include, #define und #ifdef werden ausgeführt, um den Quelltext für deon Compiler vorzubereiten. Der Präprozessor führt keine C/C++ - Syntaxanalyse durch, sundern beschränkt sich auf einfache Textersetzungen.
    Der eigentliche C/C++ - Compiler übersetzt den so vorbereiteten Quelltext in eine Objektdatei mice.obj -  Datei. Diese enthält Informationen über alle nach außen sichtbaren Symbole, den kompilierten Code der Funktionen und weitere Daten - z.B. Informationen für den Debugger
    men.cpp durchläuft den gleichen Prozeß, dabei wird die men.obj erstellt.
    Der Linker fügt die Objektdateien mit weiteren Bibliotheken zur ausführbaren Datei zusammen - z.B. mundm.exe

    [Grafik - mit Beispiel und abhängigkeiten mice.h/.cpp, men.h/.cpp]

    1.2 Was folgt daraus?
    Der Präprozessor faßt eine Implementationsdatei und ihre Abhängigkeiten #include-Dateien zu einer Übersetzungseinheit zusammen. Alle Symbole - Makros, Variablen, Funktionen - in dieser Übersetzungseinheit wirken so, als würden sie in einer Datei liegen.

    • Die Reihenfolge der #include-Dateien muß so gewählt sein, das alle Basisdefinitionen zuerst kommen, dann die darauf aufbauenden. Anders als z.B. in C# sucht sich der Compiler nicht alle Definitionen zusammen. Das ist nicht immer einfach, weiter unten mehr dazu.
    • ein "using namespace ..." in men.h beeinflußt auch mice.h. Das kann zu unerwarteten und schwer aufzulösenden Problemen führen - insbesondere, wenn verschiedene Bibliotheken zusammenkommen. Im einfachsten Fall läßt sich mice.h nicht mehr übersetzen, och schlimmer wird es, wenn sich mice.h einer Übersetzungseinheit anders verhält, weil men.h zuerst reingeladen wurde.
      Aus diesem Grund soll using namespace nicht in global Header-Dateien verwendet werden.
    • gleiches gilt für #define's

    Da der Präprozessor kein C oder C++ "versteht", gibt es einige seltsame anmutende Effekte:

    • In folgendem Code wird fälschlicherweise ein Kommentar erkannt:
      for(double * v = vec; v<vec+count; ++vec)
      *v=1.0/*v;
    • Vergißt man (bezug auf beispiel) in men.h z.B. ein abschließendes Semikolon, zeigt der Compiler den Fehler in mice.h - oder gar in einem davon hereingeladenen Header.

    _______________________________________________________________
    // --- weitere themen

    • extern + static - Deklarationen, unnamed namespaces
    • <...> vs. "..."
    • include guards, #pragma once
    • vorkompilierte Header
    • forward declarations
    • Vorschläge für regeln wer-welchen-header-reinlädt
    • Optimierung Build-Zeiten - Abhängigkeitsprüfung, Change Propagation für größere Projekte
    • "kompatibilität" von .obj- und .lib-Files
    • Code-Weitergabe: als Quellcode, als LIB, als DLL (benötigt auch einen "kleinen" Ausflug zu Varianten der Laufzeitbibliotheken, Abhängigkeiten von Compiler-Optionen, )
    • Debuginfos, Compile- und Linkzeit-Optimierungen (nur soweit das Thema betroffen ist)
    • Das ganze möchte ich (wie oben) mit kleinen Beispielen untersetzen: typische Compile-, Link- und Laufzeitfehler.
    • evtl. Kombination C- und C++ - Code in einem Projekt

    _________________________________________________________

    Was mir noch nicht klar ist:

    1. Wird das schon irgendwo hier bis zum Erbrechen behandelt? Ich hab' zwar nix gefunden, aber das will ja nichts heißen.
    2. Wäre das ein lesenswerter Artikel?
    3. Einige der Anstriche richten sich weniger an Einsteiger, je nach Menge vielleicht als Teil 2, oder vielleicht ganz weg?
    4. linux/gcc. Meine praktischen Erfahrungen damit sind minimal und außerdem aus der Computer-Steinzeit. Mir ist klar, daß obiger Abschnitt sehr Windows/VC++-lastig ist. Ich würde es aber gern so praktisch lassen, aber die gcc-typischen Unterschiede, Bezeichnungen und Konzepte hinzufügen. Da bräuchte ich noch jemande, der mir damit hilft - gern auch als Co-Autor.
    5. Wahrscheinlich sollte auch noch jemand Inhalt und Bezeichnungen auf "Kompatibilität" mit dem ANSI-Standard abklopfen. (Mein Umgang damit ist bekanntermaßen etwas lax :D)
    6. Ein knackiger Titel.

    was meint ihr?



  • Ich kann dir gerne mit dem gcc (und auf Wunsch auch mit dem Comeau -- der macht sich auch als ANSI/ISO validator gut 🙂 ) helfen.

    Klingt auf's erste eigentlich schon interessant.

    Wenn's überschneidungen gibt, dann mit irgend einem Artikel aus der uild-Systeme Reihe



  • peterchen schrieb:

    2. Wäre das ein lesenswerter Artikel?

    Es gehört zu den Sachen, die ein jeder C++ Programmier wissen sollte, von daher ist es für jeden der dies nicht kennt lesenswert.

    peterchen schrieb:

    3. Einige der Anstriche richten sich weniger an Einsteiger, je nach Menge vielleicht als Teil 2, oder vielleicht ganz weg?

    Ich würde vorschlagen entweder Einsteiger oder Fortgeschrittene als Zielgruppe zu nehmen. Beide in einem Artikel zu bedienen ist IMHO eine schlechte Idee. Wenn zwei Artikel dann würde ich die nicht Teil1 und Teil2 nennen.

    peterchen schrieb:

    4. linux/gcc. Meine praktischen Erfahrungen damit sind minimal und außerdem aus der Computer-Steinzeit. Mir ist klar, daß obiger Abschnitt sehr Windows/VC++-lastig ist. Ich würde es aber gern so praktisch lassen, aber die gcc-typischen Unterschiede, Bezeichnungen und Konzepte hinzufügen. Da bräuchte ich noch jemande, der mir damit hilft - gern auch als Co-Autor.

    Solange nur statisches Linken eine Rolle spielt halten sich die Unterschiede soweit ich weiß in Grenzen. (Ich kenne VC nicht so gut. 😉 ) Hauptsächlich muss man die Namen von manchen Sachen ändern. Zum Beispiel anstatt *.obj *.o, ausführbarere Dateien haben keine Erweiterung (bei MinGW natürlich doch *.exe) und aus .lib wird lib.a . Der BC hat es zu mindest in der Version 5.5 auch quasi gleich gemacht und halt wieder nur ein paar Namen geändert.

    Manche Compiler weichem bei inline und template Sachen von der großen Linie ab.

    Ich würde von Objektdatei reden und keine Erweiterung angeben. Eventuell am Ende eine Tabelle wo drin steht wie man welchen Typ von Datei bei welchem Compiler erkennt.

    peterchen schrieb:

    5. Wahrscheinlich sollte auch noch jemand Inhalt und Bezeichnungen auf "Kompatibilität" mit dem ANSI-Standard abklopfen. (Mein Umgang damit ist bekanntermaßen etwas lax :D)

    Auch wenn es ein ANSI-Standard ist kannst du auch von ISO-Standard sprechen. (Klingt besser 😉 ) Der Standard spezifiziert jedoch kaum etwas zu Thema Übersetzungsprozess. Da ist das allermeiste "implementation defined" von daher wird es nicht einfach werden da anzuecken.


Anmelden zum Antworten