Präprozessor



  • Danke, für deine ausführliche Antwort! 🙂

    Theoretisch kann ein Makro einfach undefiniert werden. Wenn irgendjemand versehentlich oder böswillig ein Makro, auf das du dich verlässt, in irgendeinem tief versteckten Header umdefiniert, dann viel Spaß beim Finden des Fehlers.

    Wenn ich das richtig verstanden habe, meinst du z.B.: ich definiere in meiner C-Sourcecode Datei ein Makro mit dem Namen MAKEWORD(x, y) ... Wenn ich jetzt Windef.h einfüge, gibt es dort auch ein Makro mit dem gleichen Namen von Windows. Welches Makro würde der Präprozessor nutzen? Gibt es da eine Rangfolge? Theoretisch steht das Makro aus der Windef.h vor dem eigens definierten Makro, da ja die #include <windef.h> am Anfang der Datei ersetzt wurde.

    Und wenn ich #undef Makroname nutze, ist es da egal ob das Makro vor oder nach der Zeile definiert wurde?

    Wovon genau redest du? Es gibt nur eine C Standardbibliothek.

    Ja, die meinte ich, hab mich da ein bisschen umständlich ausgedrückt. Ich meinte eig. die einzelnen Header-Files der C-Standardbibliothek, welche nur #defines nutzen.

    Dein Beispiel mit den Konstanten ist so ein Fall, das geht eben erst ab C99 mit const als echte Compilezeitkonstante, davor musste man eben defines oder enums nutzen.

    Wobei enums doch keine Präprozessordirektive ist, sondern vom Compiler verarbeitet werden, oder? Sollte man enums auch nicht mehr nutzen, oder habe ich dich da falsch verstanden?

    Ehrlich gesagt programmiere ich keine ernsthaften Programme in C. Aber wenn ich es täte, dann würde ich Makros aus den genannten Gründen meiden.

    Davon ernsthafte Programme zu schreiben, bin ich noch weit entfernt. 😃 Aber C finde ich sehr interessant und mir macht das viel mehr Spaß als z.B. in Java zu programmieren.



  • Makro schrieb:

    MAKEWORD(x, y) ... Wenn ich jetzt Windef.h einfüge, gibt es dort auch ein Makro mit dem gleichen Namen von Windows. Welches Makro würde der Präprozessor nutzen? Gibt es da eine Rangfolge? Theoretisch steht das Makro aus der Windef.h vor dem eigens definierten Makro, da ja die #include <windef.h> am Anfang der Datei ersetzt wurde.

    Und wenn ich #undef Makroname nutze, ist es da egal ob das Makro vor oder nach der Zeile definiert wurde?

    Klingt danach, als ob du dir wirklich Gedanken machst.
    Nein, der C Standard schreibt nicht vor, welche von mehreren Quelldateien inkl. ihrer Header (und damit der Präpozessor-Lauf) zuerst abgearbeitet wird.
    Du kannst darauf hoffen, dass der Compiler/Präpozessor bei doppelten Namen warnt, muss er aber nicht.
    Das #undef kannst du dir sparen, der Präprozessor/Compiler nimmt immer die 'aktuelleste', und welches die aktuellste Variante ist, ist eben unbestimmt.

    Also am besten keine eigenen Makros verwenden, und wenn doch dann wenigstens welche mit definitiv (global gesehen) eigenen Namen.
    Praktisch sind auch, wie du schon gemerkt hast, eigentlich nicht die Makros des Standards dein Problem, sondern die deiner zusätzlichen Bibliothek, die oftmals vielfach größer als die der Standardbibliothek sind (und damit größeres Konfliktpotential bieten).

    Kandidaten sind z.B. solche Laien-Versuche wie

    #define MAX(a,b) ((a)>(b)?(a):(b))
    oder gar
    #define max(a,b) ((a)>(b)?(a):(b))
    

    Also nochmal, besser gar keine Makros benutzen und auch wenig/gar keine eigenen #define.

    Gegenüber Funktionen hast du bei Makros auch den Nachteil, dass die Kopiersemantik der Parameter verloren geht, auch haben Makros keinen beeinflussbaren Scope (eben weil der Präprozessorlauf unabhängig vom eigentlichen Compiler vorher ausgeführt wird), von den exzessiv notwendigen Klammern bei Makros ganz zu schweigen.



  • Hallo 🙂

    Danke für deine Antwort. In welcher Reihenfolge der Präprozessor die Quelldateien verarbeitet, hatte ich auch schon überlegt. Damit sind gleich zwei Fragen beantwortet. 🙂

    Dann werde ich mal meine Makros durch inline-Funktionen ersetzen :D.



  • Lass das inline weg, ist sowieso nur eine Empfehlung an den Compiler, der kann eine inline-Funktion draus machen, muss er aber nicht.
    Der Compiler braucht von dir als Entwickler keine Empfehlungen für Optimierungen, er weiß sowieso alles besser, ähnlich wie bei register Variablen.



  • Achso, dann lass ich inline weg.

    Mir wird erst jetzt richtig klar, wie komplex Compiler doch eig. sind. 😃



  • Makro schrieb:

    Achso, dann lass ich inline weg.

    Dann aber auch in einer Sourcedatei definieren!


  • Mod

    Nathan schrieb:

    Makro schrieb:

    Achso, dann lass ich inline weg.

    Dann aber auch in einer Sourcedatei definieren!

    Und dann sind wir wieder bei dem Punkt, an dem es wahrscheinlich nicht mehr optimiert wird. Also doch wieder inline im Header für Minifunktionen.



  • SeppJ schrieb:

    Und dann sind wir wieder bei dem Punkt, an dem es wahrscheinlich nicht mehr optimiert wird. Also doch wieder inline im Header für Minifunktionen.

    Vielleicht können Compiler und Linker auch whole program optimization. Dann muss man sich darüber auch keine Sorgen mehr machen.
    MSVC kann es.
    GCC kann es.



  • So, ich bis nochmal 😃

    Ich hab weiterprobiert und recherchiert und mir ist noch folgendes Problem zu der Verwendung von Konstanten ein. Wenn ich diese im Header als static const NAME = wert; definiere, dann ist static der storage class specifier (Übersetzung - C-Speicherklasse ?) und const der type qualifier (Übersetzung - Typmodifizierer ?).

    Binde ich jetzt den Header (mit z.B. 100 solcher Konstanten ein), werden doch theoretisch alle Konstanten in das entsprechende Programmsegment gelegt. Laut diesem Beitrag:

    How they are stored is an implementation detail (depends on the compiler).

    For example, in the GCC compiler, on most machines, read-only variables, constants, and jump tables are placed in the text section.

    http://stackoverflow.com/questions/1576489/where-are-constant-variables-stored-in-c ist nicht genau definiert, wo diese gespeichert werden.

    Jetzt zu meiner eigentlichen Frage: Ist das speichertechnisch nicht eine Verschwendung so viele Konstanten (z.B. 100) zu speichern und ich verwende im Code dann z.B. nur 3 davon? Oder wird das seitens des Compilers optimiert? Oder habe ich da einen generellen Denkfehler drin? 😃

    Jetzt noch zur inline expansion:

    Bei dieser ist es wichtig, dass der Compiler zur Compiltime die Funktionsdefinition der Funktion vorliegen hat (ist ja i-wie logisch, ansonsten kann der Compiler ja nicht entscheiden, ob die Funkion ersetzt oder aufgerufen wird). Das erreiche ich, indem ich die Funktion direkt in der entsprechenden Datei definiere. Dann muss ich kein inline hinzufügen, oder? Wenn ich die inline Funktion im Header definiere, ist das inline Pflicht?


  • Mod

    Makro schrieb:

    Jetzt zu meiner eigentlichen Frage: Ist das speichertechnisch nicht eine Verschwendung so viele Konstanten (z.B. 100) zu speichern und ich verwende im Code dann z.B. nur 3 davon? Oder wird das seitens des Compilers optimiert? Oder habe ich da einen generellen Denkfehler drin? 😃

    Die Antwort, dass das ein Implementierungsdetails ist, ist schon richtig, wenn auch nicht nützlich. In vielen Fällen wird die Konstante aber wohl überhaupt keine Repräsentation mehr im Speicher haben, sondern direkt im Programmcode eingesetzt werden. Das ist ja gerade der Sinn der Sache.

    Bei dieser ist es wichtig, dass der Compiler zur Compiltime die Funktionsdefinition der Funktion vorliegen hat (ist ja i-wie logisch, ansonsten kann der Compiler ja nicht entscheiden, ob die Funkion ersetzt oder aufgerufen wird). Das erreiche ich, indem ich die Funktion direkt in der entsprechenden Datei definiere. Dann muss ich kein inline hinzufügen, oder? Wenn ich die inline Funktion im Header definiere, ist das inline Pflicht?

    Und wie willst du Funktionen im Header ohne inline definieren? Als static? Der Header kann schließlich von mehreren Übersetzungseinheiten eingebunden werden und du darfst nur eine Definition einer Funktion im Gesamtprogramm haben. Ausnahmen sind eben static-Funktionen (dann hast du in jeder Übersetzungseinheit eine separate Funktion, von der die anderen Einheiten nichts mit bekommen. Die Funktionen dürfen sich sogar unterscheiden) oder inline. Dies ist der Hauptzweck von inline.



  • Hi 🙂

    Die Antwort, dass das ein Implementierungsdetails ist, ist schon richtig, wenn auch nicht nützlich. In vielen Fällen wird die Konstante aber wohl überhaupt keine Repräsentation mehr im Speicher haben, sondern direkt im Programmcode eingesetzt werden. Das ist ja gerade der Sinn der Sache.

    Hmm, wenn der Header eingebunden wird, sind die static const Variablen doch aber erstmal normale Variablen oder? Die Konstanten, die ich verwende, können ja direkt ersetzt werden, aber was passiert mit den Konstanten, die ich ja als "normale" Variablen deklariere und initialisiere und nicht verwende? (Falls ich zu unverständlich schreibe, bitte sagen :))


  • Mod

    Die Konstanten, die ich verwende, können ja direkt ersetzt werden, aber was passiert mit den Konstanten, die ich ja als "normale" Variablen deklariere und initialisiere und nicht verwende?

    Da die Konstante static ist, weiß der Compiler ja, dass sie nirgends außerhalb der Übersetzungseinheit benutzt wird und braucht daher auch keinen Speicherplatz für die Konstante zu erzeugen, außer es ist aus irgendeinem Grund für die aktuelle Übersetzungseinheit nötig (etwa weil du die Adresse der Konstante benutzt). Das ist natürlich keine Garantie, dass er das nicht trotzdem tut, Implementierungsdetail eben. Aber es wäre komisch, denn der Grund, wieso es solche Konstanten gibt, ist, dass man dann eben diese Art von Optimierung durchführen kann.

    Hmm, wenn der Header eingebunden wird, sind die static const Variablen doch aber erstmal normale Variablen oder?

    Wir reden hier schon von C99, oder? Ich bin den ganzen Thread über davon ausgegangen, da erst ab C99 const-Variablen überhaupt richtige Compilezeitkonstanten sind. Wenn wir von C89 reden, dann kannst du den gesamten Thread vergessen.



  • Da die Konstante static ist, weiß der Compiler ja, dass sie nirgends außerhalb der Übersetzungseinheit benutzt wird und braucht daher auch keinen Speicherplatz für die Konstante zu erzeugen, außer es ist aus irgendeinem Grund für die aktuelle Übersetzungseinheit nötig (etwa weil du die Adresse der Konstante benutzt). Das ist natürlich keine Garantie, dass er das nicht trotzdem tut, Implementierungsdetail eben. Aber es wäre komisch, denn der Grund, wieso es solche Konstanten gibt, ist, dass man dann eben diese Art von Optimierung durchführen kann.

    Alles klar, damit ist meine Frage beantwortet, danke dir :).

    Wir reden hier schon von C99, oder? Ich bin den ganzen Thread über davon ausgegangen, da erst ab C99 const-Variablen überhaupt richtige Compilezeitkonstanten sind. Wenn wir von C89 reden, dann kannst du den gesamten Thread vergessen.

    Jo, wir reden hier von C99.



  • Ich bin immer wieder fasziniert was die Leute hier so an Wissen mitbringen 🙂
    @SeppJ , @hustbear und so:)

    Respekt:) 👍 👍



  • SeppJ schrieb:

    da erst ab C99 const-Variablen überhaupt richtige Compilezeitkonstanten sind.

    ? Zeig mal, wo das steht.
    const bleibt const in C, egal ob C89 oder C99, Compilezeitkonstanten sind sie (im Gegensatz zu C++) beide Male nicht.
    Die einzigen Unterschiede, die mir zw. C89 und C99 gerade einfallen sind VLA-Dimensionen und Initialisierer bei static Objekten.

    const int i = 10;
    int a[i];
    /* funktioniert nur in C99, heißt aber noch lange nicht, dass i eine Konstante zur Compilezeit ist. */
    
    const double pi = 4 * atan(1);
    /* ist konform in C89 und C99, weil pi eben keine Compilezeit-Konstante ist, da sie erst zur Laufzeit initialisiert wird/werden kann,
    und erst danach dann (also zur Laufzeit) unveränderlich/konstant ist. */
    

  • Mod

    Wutz schrieb:

    SeppJ schrieb:

    da erst ab C99 const-Variablen überhaupt richtige Compilezeitkonstanten sind.

    ? Zeig mal, wo das steht.
    const bleibt const in C, egal ob C89 oder C99, Compilezeitkonstanten sind sie (im Gegensatz zu C++) beide Male nicht.
    Die einzigen Unterschiede, die mir zw. C89 und C99 gerade einfallen sind VLA-Dimensionen und Initialisierer bei static Objekten.

    Tatsächlich. Da bin ich wohl selber gewolft worden. Man sieht meine Aussage sehr oft in verschiedenen Quellen, die aber offenbar wohl alle eher mit Vorsicht zu genießen sind. Aber im Standard ist es tatsächlich nicht erlaubt und meine soeben gemachten Tests (ich habe das vorher nie wirklich geprüft) schlagen auch tatsächlich fehl.



  • Bedeutet das jetzt, dass diese Art der Optimierung:

    Da die Konstante static ist, weiß der Compiler ja, dass sie nirgends außerhalb der Übersetzungseinheit benutzt wird und braucht daher auch keinen Speicherplatz für die Konstante zu erzeugen, außer es ist aus irgendeinem Grund für die aktuelle Übersetzungseinheit nötig (etwa weil du die Adresse der Konstante benutzt). Das ist natürlich keine Garantie, dass er das nicht trotzdem tut, Implementierungsdetail eben. Aber es wäre komisch, denn der Grund, wieso es solche Konstanten gibt, ist, dass man dann eben diese Art von Optimierung durchführen kann.

    nicht durchgeführt wird? Oder hat das erstmal primär mit static zu tun (hab das in deinem Text nicht ganz rauslesen können, ob du hier als Konstante eine Variable meinst, die auf jeden Fall "const" als Qualifier haben muss) und nicht mit const, für das ich als Definition immer nur read-only gefunden habe (http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html), was nicht heißt, dass man das nicht umgehen kann.


  • Mod

    Doch, diese Optimierung kann immer noch durchgeführt werden. Und ja, das static spielt dabei eine wichtige Rolle. Es würde sogar ohne das const gehen, denn der Compiler kann schließlich sehen, dass die Variable nirgendwo verändert wird.



  • Danke @SeppJ und auch nochmal danke @Wutz 🙂


Anmelden zum Antworten