Vor- und Nachteile von #define vs. Konstanten? [duplicate]

Lesezeit: 9 Minuten

Benutzer-Avatar
Matt

Kann jemand auf die Vor- und Nachteile der Verwendung hinweisen #define gegen Konstanten? Die meisten meiner Arbeiten werden in C und Objective-C durchgeführt.

  • #define‘s respektieren den Geltungsbereich nicht.

    – GManNickG

    22. Dezember 2009 um 3:43 Uhr

Benutzer-Avatar
jamesdlin

Wie 0A0D erwähnt, gibt es #defines, enumsund const Variablen. Es ist erwähnenswert const-Qualifizierte Variablen werden in C nicht als Kompilierungskonstanten betrachtet und können daher unter Umständen nicht verwendet werden (z. B. beim Deklarieren der Größe eines Arrays).

enum Konstanten sind jedoch Kompilierzeitkonstanten. Für ganzzahlige Werte ist es meiner Meinung nach normalerweise besser, sie zu bevorzugen enums Über const Variablen vorbei #define.

  • Ich bin schockiert, dass die einzige Antwort, die richtig auf den klaren Nachteil der Verwendung hingewiesen hat const in C (und damit auch Objective-C; beachten Sie, dass es bei dieser Frage nicht um C++ geht!) wird nicht nach oben aktualisiert. +1, und wünschte, ich könnte das mehr geben.

    – Pavel Minaev

    22. Dezember 2009 um 4:46 Uhr

  • Um es zusammenzufassen, lautet die kurze Regel: Verwenden Sie für ganzzahlige Konstanten anonyme Aufzählungen. Für alles andere verwenden const. Nicht verwenden #define außer für richtige Makros oder bedingt eingeschlossene Token.

    – Pavel Minaev

    22. Dezember 2009 um 7:30 Uhr

  • @ Michael: enum Typen ohne Typnamen. Zum Beispiel, enum { foo = 42 }; ist anonym; enum bar { foo = 42 }; ist nicht.

    – jamesdlin

    15. Januar 2012 um 6:52 Uhr

Eigentlich gibt es drei Möglichkeiten, solche Konstanten zu definieren,

  • definiert

  • Aufzählungen
  • const-Variablen

In C ist alles ein Int, sofern nicht anders angegeben. Ich bevorzuge Aufzählungen, wenn ich eine Reihe verwandter ganzzahliger Konstanten habe. Enums sind eindeutig vorzuziehen, wenn Sie sich nicht um die Werte kümmern. Aber selbst wenn Sie die Werte für alle Konstanten angeben müssen, gefällt mir die gedankliche Gruppierung einer Aufzählung. Code dokumentiert sich besser, wenn Sie den Typ haben, z

Error MyFunc();

gibt eindeutig einen aus einem bestimmten Satz von Fehlercodes zurück, wohingegen

int MyFunc()

könnte eine der #define’d-Liste für Unix-Errno zurückgeben, oder vielleicht etwas anderes, oder vielleicht diese plus einige idiosynkratische Werte — wer weiß? Wenn Sie mehr als einen Satz von Rückgabecodes haben, welchen Satz verwendet diese Funktion?

Der spezifischere Enum-Typ-Name hilft der Tag-Funktion in Ihrem Editor, Greps, Debugging und so weiter.

Ein strikter Lint kann Ihnen einige Warnungen über die Verwendung von Aufzählungen als Ganzzahlen geben, z. B. wenn Sie oder oder sie hinzufügen oder eine Aufzählung an eine Ganzzahl übergeben.

Ein const-Objekt unterscheidet sich von einem enum oder einem #define, insbesondere in C. In ANSI C nimmt ein const int genauso viel Platz ein wie ein reguläres int; Die meisten Compiler generieren auch Zeigerreferenzen auf diese Adresse, anstatt den Wert einzufügen. Infolgedessen verwende ich selten const ints in C. (C++ hat eine etwas andere Semantik, daher sind die Auswahlmöglichkeiten dort unterschiedlich.)

Jeder Compiler, den ich je verwendet habe, hat die Möglichkeit, Enums auf dem kleinstmöglichen Platz zu speichern. Normalerweise ist es sogar die Standardoption. Um bei Verwendung einer solchen Option breitere Aufzählungen zu erzwingen, gebe ich normalerweise einen zusätzlichen unsigned Wert ein:

typedef enum
{
    MyEnumA,
    MyEnumB,

    MyEnumForce16 = 7fff
} MyEnum;

Die Verwendung einer Aufzählungskonstante (enum) hat viele Vorteile gegenüber der Verwendung des traditionellen symbolischen Konstantenstils von #define. Zu diesen Vorteilen gehören ein geringerer Wartungsbedarf, eine verbesserte Lesbarkeit des Programms und eine bessere Debugging-Fähigkeit.

1) Der erste Vorteil besteht darin, dass Aufzählungskonstanten automatisch vom Compiler generiert werden. Umgekehrt müssen symbolischen Konstanten vom Programmierer manuell Werte zugewiesen werden.

Wenn Sie beispielsweise einen Aufzählungskonstantentyp für Fehlercodes hätten, die in Ihrem Programm auftreten könnten, könnte Ihre Aufzählungsdefinition etwa so aussehen:

enum Error_Code
{
OUT_OF_MEMORY,
INSUFFICIENT_DISK_SPACE,
LOGIC_ERROR,
FILE_NOT_FOUND
};

Im vorangehenden Beispiel wird OUT_OF_MEMORY vom Compiler automatisch der Wert 0 (Null) zugewiesen, da es in der Definition an erster Stelle steht. Der Compiler weist dann den aufgezählten Konstanten automatisch Zahlen zu, wodurch INSUFFICIENT_DISK_SPACE gleich 1, LOGIC_ERROR gleich 2 und FILE_NOT_FOUND gleich 3 wird, usw. Wenn Sie dasselbe Beispiel mit symbolischen Konstanten angehen würden, würde Ihr Code in etwa so aussehen:

#define OUT_OF_MEMORY 0
#define INSUFFICIENT_DISK_SPACE 1
#define LOGIC_ERROR 2
#define FILE_NOT_FOUND 3

Jede der beiden Methoden kommt zum gleichen Ergebnis: vier Konstanten, denen numerische Werte zugewiesen werden, um Fehlercodes darzustellen. Berücksichtigen Sie jedoch den Wartungsaufwand, wenn Sie zwei Konstanten hinzufügen würden, um die Fehlercodes darzustellen DRIVE_NOT_READY und CORRUPT_FILE. Wenn Sie die Aufzählungskonstantenmethode verwenden, würden Sie diese beiden Konstanten einfach irgendwo in der Aufzählungsdefinition platzieren. Der Compiler würde für diese Konstanten zwei eindeutige Werte generieren. Bei Verwendung der symbolischen Konstantenmethode müssten Sie diesen Konstanten manuell zwei neue Nummern zuweisen. Außerdem sollten Sie sicherstellen, dass die Nummern, die Sie diesen Konstanten zuweisen, eindeutig sind.

2) Ein weiterer Vorteil der Verwendung der Aufzählungskonstantenmethode besteht darin, dass Ihre Programme besser lesbar sind und daher von anderen besser verstanden werden können, die Ihr Programm möglicherweise später aktualisieren müssen.

3) Ein dritter Vorteil der Verwendung von Aufzählungskonstanten besteht darin, dass einige symbolische Debugger den Wert einer Aufzählungskonstante ausgeben können. Umgekehrt können die meisten symbolischen Debugger den Wert einer symbolischen Konstante nicht drucken. Dies kann eine enorme Hilfe beim Debuggen Ihres Programms sein, denn wenn Ihr Programm an einer Zeile angehalten wird, die eine Aufzählung verwendet, können Sie diese Konstante einfach untersuchen und wissen sofort ihren Wert. Andererseits, weil die meisten Debugger nicht drucken können #define Werten, müssten Sie höchstwahrscheinlich nach diesem Wert suchen, indem Sie ihn manuell in einer Header-Datei nachschlagen.

Das #define -Anweisung ist eine Pre-Compiler-Direktive. Technisch gesehen ist jede Zeile, die mit einem # beginnt, etwas, worauf der Precompiler reagieren muss. Der Precompiler ersetzt alle Instanzen des definierten Tokens durch seine Definition. Also das tun:

#define DELAY 40
for (i=0;i<DELAY;i++) {
    for (j=0;j<DELAY;j++) {
        asm NOP;
    }
}

ist genau dasselbe (was den Compiler betrifft):

for (i=0;i<40;i++) {
    for (j=0;j<40;j++) {
        asm NOP;
    }
}

Wenn der Compiler Maschinencode generiert, sieht er die Zahl 40 und verwendet den unmittelbaren Adressierungsmodus, um mit dem Akkumulator zu vergleichen. Die Zahl 40 wird so oft im Code gespeichert, wie Sie darauf verweisen. In diesem Fall ist es das Doppelte. Hier ist die von CodeWarrior Ver5 generierte Assembly:

7:    char i,j;
    8:    for (i=0;i<DELAY;i++) {
  0002 95       [2]             TSX   
  0003 7f       [2]             CLR   ,X
  0004          [5]     L4:     
    9:      for (j=0;j<DELAY;j++) {
  0004 6f01     [3]             CLR   1,X
  0006          [5]     L6:     
   10:        asm NOP;
  0006 9d       [1]             NOP   
  0007 6c01     [4]             INC   1,X
  0009 e601     [3]             LDA   1,X
  000b a128     [2]             CMP   #40  ;<---- notice opcode a1 and immediate constant 40, which is $28 in hexadecimal
  000d 25f7     [3]             BCS   L6
  000f 7c       [3]             INC   ,X
  0010 f6       [2]             LDA   ,X
  0011 a128     [2]             CMP   #40  ;<---- and here it is again.
  0013 25ef     [3]             BCS   L4
   11:      }
   12:    }
   13:  }

  • +1 für die Auflistung aller verfügbaren Optionen, aber es lohnt sich, den Nutzen der Verwendung zu erweitern enumund Nachteil von const verglichen mit enum und #define.

    – Pavel Minaev

    22. Dezember 2009 um 4:49 Uhr

Mit Konstanten können Sie einen Datentyp angeben, was (normalerweise) von Vorteil ist. Makros sind viel flexibler und können Sie daher in viel größere Schwierigkeiten bringen, wenn Sie nicht aufpassen.

Best Practice ist es, so viel wie möglich Konstanten zu verwenden und #define nur dann zu verwenden, wenn Sie wirklich ein Makro benötigen, nicht nur einen benannten Literalwert.

  • -1 dafür, dass du das nicht erwähnt hast const in C ist keine echte (Kompilierzeit-)Konstante und kann daher nicht in verwendet werden case Labels, als Array-Größen und in anderen Kontexten, in denen ein konstanter Ausdruck erforderlich ist. Es gibt einen Grund dafür #define wird so stark in C verwendet (und const wird viel häufiger in C++ verwendet), und es zu beschönigen, ist eine schlechte Antwort auf diese Frage.

    – Pavel Minaev

    22. Dezember 2009 um 4:48 Uhr

  • Ich habe vergessen, dass const in C schwächer ist, wie Sie erwähnt haben, aber ich denke, die beste Vorgehensweise besteht immer noch darin, wann immer möglich consts zu verwenden.

    – Asher Dunn

    22. Dezember 2009 um 5:21 Uhr

  • IMO ist die beste Option, tatsächlich eine anonyme zu verwenden enum für diese ganzzahligen Konstantenausdrücke – es ist irgendwie typisiert, es hat einen Bereich, keine Namenskonflikte usw., aber es ist auch eine ganzzahlige Konstante. Für alles andere (für das es keine Rolle spielt, wann die Kompilierzeit ist – Floats, Strings usw.) const ist gut. Beachten Sie auch, dass anonyme Enums in ANSI C89 enthalten sind, obwohl viele Leute das Gegenteil glauben (da anonyme Strukturen nicht vorhanden sind).

    – Pavel Minaev

    22. Dezember 2009 um 7:28 Uhr

  • Ein weiterer kleiner Nachteil von enum ist, dass jedes Zahlensuffix wie U oder LL vom Compiler gelöscht wird, da eine Aufzählung immer eine ist int in Standard-C (obwohl einige Compiler vorschlagen, kleinere Typen für eingebettete sw zu verwenden).

    – Calandoa

    6. November 2012 um 12:09 Uhr


  • Können Sie ein Beispiel für eine Situation geben, in der Sie “wirklich ein Makro brauchen”?

    – Arc676

    30. Dezember 2014 um 8:40 Uhr

Konstanten haben den Vorteil, dass sie typisiert werden, sodass eine falsche Verwendung zur Kompilierzeit entdeckt werden kann. Es mag Ihnen egal sein, aber Konstanten nehmen Platz im Speicher ein, während #defines dies nicht tun (da sie ersetzt werden, bevor die eigentliche Kompilierung erfolgt).

Konstanten folgen Typsicherheitsmaßnahmen, #defines werden direkt ersetzt. Wie GMan sagte, respektieren #define den Geltungsbereich nicht.

Erklärung für #define:Ein #define ist entweder ein Sofortwert oder ein Makro.

Erklärung für konstant:Eine Konstante ist ein beliebiger Wert, der sich nie ändern kann.

Sie können einen Zeiger auf eine Konstante löschen, aber nicht auf ein #define, obwohl ein #define ein Zeiger sein könnte für zB: #define ADDRESS ((int *)0x0012)

Warum wir also Konstante verwenden sollten, ist wie folgt:

  • Sie befolgen die Scoping-Regeln der Sprache
  • Sie können sie im Debugger sehen
  • Sie können ihre Adresse notfalls notieren
  • Sie können sie bei Bedarf per const-Referenz übergeben
  • sie erstellen keine neuen “Schlüsselwörter” in Ihrem Programm.

Kurz gesagt, konstante Bezeichner verhalten sich so, als wären sie Teil der Sprache, weil sie Teil der Sprache sind.

Innerhalb eines Moduls könnte ein C-Compiler eine Konstante so optimieren, als wäre sie ein #define, wenn keine Zeiger auf die Konstante deklariert sind. In Bezug auf die CPU würde die Konstante zu einem “unmittelbaren” Wert. Andere Alternativen sind, dass eine konstante Variable im Codebereich im Gegensatz zum Datenbereich platziert werden könnte, da sie sich nicht ändert. Auf einigen Maschinen konnte das Deklarieren eines Ponters für eine Konstante eine Ausnahme verursachen, wenn Sie versuchten, die Konstante über den Zeiger zu ändern.

Es gibt Fälle, in denen #define erforderlich ist, aber Sie sollten es im Allgemeinen vermeiden, wenn Sie die Wahl haben. Sie sollten basierend auf dem Geschäftswert abwägen, ob Sie const oder #define verwenden: Zeit, Geld, Risiko.

Benutzer-Avatar
anijhaw

Const ist ein Objekt, dessen Adresse Sie zum Beispiel nehmen können. Außerdem ist es typsicher, dh der Compiler weiß, welchen Typ die Konstante hat. Obiges gilt nicht für #define.

1092950cookie-checkVor- und Nachteile von #define vs. Konstanten? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy