Variablentyp, Wertebereich & verwendete Bytes der #define-Direktive?



  • Hey

    Lerne gerade C & habe mir dazu das Buch 'C in 21 Tagen' besorgt.

    Wenn ich symbolische Konstanten erstelle, habe ich 2 Möglichkeiten, die '#define'-Direktive oder mit dem Schlüsselwort 'const'.

    Mit 'const' kann ich den Variablentyp selber wählen, das heißt, ich weiß durch den Variablentyp also welcher Wertebereich mir zur Verfügung steht & wieviel Bytes verwendet werden.

    const int MAXIMUM = 100;
    

    Variablentyp > int
    Wertebereich > -32768 bis 32767
    verwendet Bytes > 2

    Das Buch rät mir bei symbolischen Konstanten aber, '#define' zu verwenden.
    Wie is das bei der #define-Direktive mit Variablentyp, Wertebereich & verwendeten Bytes?

    #define MAXIMUM 100
    

    Variablentyp > ?
    Wertebereich > ?
    verwendete Bytes > ?

    grTz...



  • Der Preprozessor macht reinen Textersatz.

    R3FRESH schrieb:

    Variablentyp > ?
    Wertebereich > ?
    verwendete Bytes > ?

    Das hängt von der Variablen ab, bei der du mit MAXIMUM arbeitest.

    char c = MAXIMUM;
    int  i = MAXIMUM;
    double d = MAXIMUM;
    int j = 0;
    if (j < MAXIMUM) ...
    

    Alles möglich.



  • Wertebereich > -32768 bis 32767
    verwendet Bytes > 2

    Das weisst du nicht. Das kann auch ein größerer Bereich und mehr Bytes sein.

    Bei define ist das so, als ob du die Konstante direkt als Wert in den Programmcode einfügen würdest. Im Allgemeinen wird dann einfach das passendste genommen, in deinem Beispiel wohl (vermute ich mal) int.



  • Dein Buch hat absolut Recht. Dieses const-Kennwort müsste man verbieten. So liegen doch die Vorteile von den Direktiven auf der Hand:

    • Unbegrenzter Wertebereich
    • Generischer Wertetyp
    • Null Speicher

    Dazu kommen noch weitere Vorteile, die in deinem Buch nicht genannt werden:

    • Sichtbarkeit, Namenskollisionen werden vermieden
    • Kann nachträglich viel freier verändert werden
    • Aus Konsistenzgründen schreibt man lieber alles in Makros.


  • Interessant ^^

    Tjou, dann bleibe ich bei '#define'.
    Die Variablen mit denen ich arbeite, zB 'MAXIMUM', müssen dann aber noch am Anfang des Quellcodes initialisiert werden, wie es Dirk vorgemacht hat?!



  • Der will dich auf den Arm nehmen. Keine Namenskollisionen bei Makros? Lustige Vorstellung.

    Makros machen stumpf Textersetzung.

    #define MAXIMUM 100
    

    bedeutet, dass danach alle Vorkommen von MAXIMUM im Quelltext durch 100 ersetzt werden. MAXIMUM ist dementsprechend vom Typ int, für den der Standard einen Wertebereich von mindestens -32768 bis 32767 (-215 bis 215 - 1, d.h. 16 Bit breit) garantiert, der aber auf heutigen Maschinen in der Regel (Mikrocontroller ausgenommen) Werte von -2147483648 bis 2147483647 aufnehmen kann (d.h. 32 bit).

    Ein Literal ist nicht typlos, und das ist wichtig. Woher diese Idee mit "generischen Typen" kommt, ist mir völlig schleierhaft. Jedenfalls haben Literale Typen wie folgt:

    int                a = 100;
    long               b = 100L;
    long long          c = 100LL;
    unsigned           d = 100U;
    unsigned long      e = 100UL;
    unsigned long long f = 100ULL;
    
    double             g = 3.141;
    float              h = 3.141F;
    long double        i = 3.141L;
    

    das überträgt sich auf Makros. Bei Variablenzuweisungen innerhalb eines bestimmten Wertebereichs ist die Angabe eines entsprechenden Suffixes nicht zwingend notwendig, weil die Typumwandlung implizit passiert, aber es kann unter Umständen wichtig werden. Beispielsweise wird

    unsigned long long x = 3000000000; // 3 Milliarden
    

    auf Maschinen mit 32-Bit-Integern zu einem Compilerfehler führen, weil das Literal versucht, vom Typ int zu sein, der den Wert nicht fassen kann. Ähnlich wird

    unsigned long long x = 1 << 32;
    

    dort nicht machen, wonach es auf den ersten Blick aussieht.

    Der Vorteil von Konstanten gegenüber Makros ist, dass sie ein Scope haben, d.h.

    {
      int const max = 10;
    }
    // max ist hier nicht mehr bekannt
    

    und dementsprechend nicht Code verändern, der nach ihnen kommt. Wer mal mit windows.h gearbeitet hat, kennt die Problematik - dort werden zwei Makros min() und max() definiert, die regelmäßig nachfolgende Header zerschießen.

    Der Nachteil, jedenfalls auf Vor-C99-Compilern, ist, dass sie nicht zur Angabe der Größe eines Arrays benutzt werden können.

    int const dimension = 100;
    int foo[dimension]; // Darf ein C89-Compiler ablehnen
    
    #define DIMENSION 100
    int foo[DIMENSION]; // Darf er nicht ablehnen
    

    Der Grund dafür ist, dass Konstanten in C keine Compilezeit-Konstanten sein müssen. Beispielsweise

    void foo(int elems) {
      int const dimension = elems * 2;
      int foo[dimension]; // Ups.
    }
    

    Deswegen wird das erst mit der Einführung von Arrays variabler Länge in C99 möglich. Problematisch ist das, weil ein gängiger C-Compiler - MSVC - bis heute C99 nicht beherrscht. Wobei auch der, wenn ich das richtig im Kopf habe, VLAs kennt, es also eigentlich keinen wirklich guten Grund mehr gibt, Konstanten als Makros zu definieren.

    Allerdings muss man anerkennen, dass Makros für diesen Zweck in C durchaus üblich sind. Es ist aber von größer Wichtigkeit, eindeutige Bezeichner zu wählen, sonst läufst du in Gefahr, dich mit Fremdcode zu beißen. MAXIMUM ist ein schlechter Makroname, weil jemand anderes auf die Idee kommen kann, ihn auch zu benutzen. Eine verbreitete Methode, das Problem von Kollisionen zu umgehen, ist ein Präfix - quasi ein Namensraum. Boost.Preprocessor nennt alle seine Makros beispielsweise BOOST_PP_EIGENTLICHER_NAME. Du tätest gut daran, ähnlich zu verfahren.


Anmelden zum Antworten