Ich bin verwirrt darüber, wann Makros oder Aufzählungen verwendet werden sollen. Beide können als Konstanten verwendet werden, aber was ist der Unterschied zwischen ihnen und was ist der Vorteil von beiden? Hängt es irgendwie mit dem Compiler-Level zusammen oder nicht?
Was macht eine bessere Konstante in C, ein Makro oder eine Aufzählung?
Varun Chhangani
Sergej Kalinitschenko
In Bezug auf die Lesbarkeit sind Enumerationen bessere Konstanten als Makros, da zusammengehörige Werte gruppiert werden. Zusätzlich, enum
definiert einen neuen Typ, sodass die Leser Ihres Programms leichter herausfinden können, was an den entsprechenden Parameter übergeben werden kann.
Vergleichen
#define UNKNOWN 0
#define SUNDAY 1
#define MONDAY 2
#define TUESDAY 3
...
#define SATURDAY 7
zu
typedef enum {
UNKNOWN,
SUNDAY,
MONDAY,
TUESDAY,
...
SATURDAY,
} Weekday;
Es ist viel einfacher, Code wie diesen zu lesen
void calendar_set_weekday(Weekday wd);
als das
void calendar_set_weekday(int wd);
weil Sie wissen, welche Konstanten übergeben werden dürfen.
-
In der Tat, aber man könnte genauso bei den Makros bleiben, und dann
typedef int Weekday;
da weder typedefs noch enums (in diesem Sinne) in C typsicher sind …– Oliver Charlesworth
15. Juni 2013 um 16:14 Uhr
-
@ H2CO3: Ein
gdb
Ich benutzep (int)Thursday
oderp (Weekday)3
.– jxh
15. Juni 2013 um 16:21 Uhr
-
@VarunChhangani Gefällt mir
enum foo { BAR = 3, BAZ = 1 << 9 }
? Ja– Kevin
15. Juni 2013 um 18:51 Uhr
-
Übrigens ist es legal, nach dem letzten Element in der Aufzählungsliste ein Komma zu setzen, daher ist die seltsame Kommaformatierung nicht erforderlich.
– jamesdlin
15. Juni 2013 um 18:57 Uhr
-
@jamesdlin Ich habe kürzlich herausgefunden, dass es so war nicht legal in ANSI C. Es hat mich überrascht (ich dachte immer, der Hauptvorteil wäre, dass es die Codegenerierung ein bisschen einfacher macht), aber da haben Sie es.
– Thomas
18. Juni 2014 um 7:09 Uhr
Ein Makro ist eine Präprozessorsache, und der kompilierte Code hat keine Ahnung von den von Ihnen erstellten Bezeichnern. Sie wurden bereits durch den Präprozessor ersetzt, bevor der Code den Compiler erreicht. Eine Aufzählung ist eine Entität zur Kompilierzeit, und der kompilierte Code behält die vollständigen Informationen über das Symbol bei, die im Debugger (und anderen Tools) verfügbar sind.
Bevorzugen Sie Aufzählungen (wenn Sie können).
-
Ich denke, dass Enum beim Debuggen im Vergleich zu Makros komfortabler ist.
– Varun Chhangani
15. Juni 2013 um 18:47 Uhr
-
Nicht streiten, sondern mit
gcc -g3
können Sie eine gdb-freundliche ausführbare Datei kompilieren, die Informationen über Makros enthält.– Braden Best
29. September 2015 um 0:41 Uhr
Kaz
In C ist es am besten, Aufzählungen für tatsächliche Aufzählungen zu verwenden: wenn eine Variable einen von mehreren Werten enthalten kann, denen Namen gegeben werden können. Ein Vorteil von Aufzählungen besteht darin, dass der Compiler einige Überprüfungen durchführen kann, die über die Anforderungen der Sprache hinausgehen, z. B. dass einer switch-Anweisung für den Aufzählungstyp einer der Fälle nicht fehlt. Die Enum-Identifikatoren werden auch in die Debugging-Informationen weitergegeben. In einem Debugger können Sie den Bezeichnernamen als Wert einer Aufzählungsvariablen und nicht nur als numerischen Wert sehen.
Aufzählungen können nur für den Nebeneffekt verwendet werden, symbolische Konstanten vom ganzzahligen Typ zu erstellen. Zum Beispiel:
enum { buffer_size = 4096 }; /* we don't care about the type */
Diese Praxis ist nicht so weit verbreitet. Für eine Sache, buffer_size
wird als Ganzzahl und nicht als Aufzählungstyp verwendet. Ein Debugger wird nicht gerendert 4096
hinein buffer_size
, da dieser Wert nicht als Aufzählungstyp dargestellt wird. Wenn Sie einige erklären char array[max_buffer_size];
dann sizeof array
wird nicht als angezeigt buffer_size
. In dieser Situation verschwindet die Aufzählungskonstante zur Kompilierzeit, also könnte es genauso gut ein Makro sein. Und es gibt Nachteile, wie z. B. nicht in der Lage zu sein, seinen genauen Typ zu kontrollieren. (In einigen Situationen, in denen die Ausgabe der Vorverarbeitungsstufen der Übersetzung als Text erfasst wird, kann es einen kleinen Vorteil geben. Ein Makro wird hingegen zu 4096 buffer_size
wird so bleiben buffer_size
).
Ein Präprozessorsymbol ermöglicht uns dies:
#define buffer_size 0L /* buffer_size is a long int */
Beachten Sie, dass verschiedene Werte von C’s <limits.h>
wie UINT_MAX
sind Präprozessorsymbole und keine Aufzählungssymbole, und das aus gutem Grund, denn diese Bezeichner müssen einen genau bestimmten Typ haben. Ein weiterer Vorteil eines Präprozessorsymbols besteht darin, dass wir es auf Vorhandensein testen oder sogar Entscheidungen basierend auf seinem Wert treffen können:
#if ULONG_MAX > UINT_MAX
/* unsigned long is wider than unsigned int */
#endif
Natürlich können wir auch Aufzählungskonstanten testen, aber nicht so, dass wir globale Deklarationen basierend auf dem Ergebnis ändern können.
Enumerationen sind auch für Bitmasken schlecht geeignet:
enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }
es macht einfach keinen Sinn, denn wenn die Werte mit einem bitweisen OR kombiniert werden, erzeugen sie einen Wert, der außerhalb des Typs liegt. Solcher Code verursacht auch Kopfschmerzen, wenn er jemals nach C++ portiert wird, das (etwas mehr) typsichere Enumerationen hat.
-
Können wir den Wert von enum zur Kompilierzeit ändern und const ist gleich enum oder macro
– Varun Chhangani
15. Juni 2013 um 18:53 Uhr
Jens
Beachten Sie, dass es einige Unterschiede zwischen Makros und Aufzählungen gibt, und jede dieser Eigenschaften kann sie als bestimmte Konstante (un)geeignet machen.
- Aufzählungen sind signiert (kompatibel mit int). In jedem Kontext, in dem ein vorzeichenloser Typ erforderlich ist (denken Sie insbesondere an bitweise Operationen!), sind Aufzählungen out.
- Wenn long long breiter als int ist, passen große Konstanten nicht in eine Aufzählung.
- Die Größe einer Aufzählung ist (normalerweise)
sizeof(int)
. Für Arrays mit kleinen Werten (bis zu sagen,CHAR_MAX
) möchten Sie vielleicht achar foo[]
eher als einenum foo[]
Reihe. - Aufzählungen sind ganze Zahlen. Du kannst nicht haben
enum funny_number { PI=3.14, E=2.71 }
. - Aufzählungen sind eine C89-Funktion; K&R-Compiler (zugegebenermaßen uralt) verstehen sie nicht.
AnT steht zu Russland
Wenn das Makro richtig implementiert ist (dh es leidet nicht unter Assoziationsproblemen, wenn es ersetzt wird), dann gibt es keinen großen Unterschied in der Anwendbarkeit zwischen Makro- und Aufzählungskonstanten in Situationen, in denen beide sind anwendbar, dh in Situationen, in denen Sie speziell vorzeichenbehaftete ganzzahlige Konstanten benötigen.
Im Allgemeinen bieten Makros jedoch eine viel flexiblere Funktionalität. Aufzählungen legen Ihren Konstanten einen bestimmten Typ auf: Sie haben einen Typ int
(oder möglicherweise ein größerer vorzeichenbehafteter Integer-Typ), und sie werden immer vorzeichenbehaftet sein. Mit Makros können Sie Konstantensyntax, Suffixe und/oder explizite Typkonvertierungen verwenden, um eine Konstante eines beliebigen Typs zu erzeugen.
Aufzählungen funktionieren am besten, wenn Sie eine Gruppe eng verbundener sequenzieller ganzzahliger Konstanten haben. Sie funktionieren besonders gut, wenn Sie sich überhaupt nicht um die tatsächlichen Werte der Konstanten kümmern, dh wenn Sie sich nur darum kümmern, dass sie sie haben etwas wohlerzogene einzigartige Werte. In allen anderen Fällen sind Makros die bessere Wahl (oder im Grunde die einzige Wahl).
In praktischer Hinsicht gibt es kaum einen Unterschied. Sie sind gleichermaßen als Konstanten in Ihren Programmen verwendbar. Einige mögen aus stilistischen Gründen das eine oder andere bevorzugen, aber ich kann mir keinen technischen Grund vorstellen, das eine dem anderen vorzuziehen.
Ein Unterschied besteht darin, dass Sie mit Makros den ganzzahligen Typ verwandter Konstanten steuern können. Aber ein enum
wird ein verwenden int
.
#define X 100L
enum { Y = 100L };
printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */
pmor
enum
hat einen Vorteil: Blockumfang:
{ enum { E = 12 }; }
{ enum { E = 13 }; }
Bei Makros besteht Bedarf #undef
.
-
Ich würde noch nie erwägen Sie, eine zu geben
enum
Geben Sie verschiedene Werte an verschiedenen Stellen in derselben Codebasis ein. Jede Art von “Vorteil” in irgendeiner Weise, Form oder Form.– Andreas Henle
21. Februar um 12:00 Uhr
-
Wenn A ein Feature anbietet und B nicht, dann hat A in der Regel einen Vorteil gegenüber B.
– pmor
22. Februar um 18:23 Uhr
-
Sie können auch eine Bowlingkugel auf Ihren Fuß fallen lassen. Das ist kein Vorteil.
– Andreas Henle
22. Februar um 18:27 Uhr
Ich denke, das ist eine großartige Frage, wenn Sie es so betrachten: Was sind die Unterschiede zwischen der Verwendung von Makros und Aufzählungen zum Definieren von Konstanten in C?
– Sam Harwell
15. Juni 2013 um 16:10 Uhr