Was macht eine bessere Konstante in C, ein Makro oder eine Aufzählung?

Lesezeit: 7 Minuten

Benutzeravatar von Varun Chhangani
Varun Chhangani

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?

  • 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

Benutzeravatar von Sergey Kalinichenko
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 gdbIch benutze p (int)Thursdayoder p (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 -g3können Sie eine gdb-freundliche ausführbare Datei kompilieren, die Informationen über Makros enthält.

    – Braden Best

    29. September 2015 um 0:41 Uhr

Benutzeravatar von Kaz
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

Benutzeravatar von Jens
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 a char foo[] eher als ein enum 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 mit Russlands Benutzer-Avatar
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 */

Benutzeravatar von pmor
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

1402500cookie-checkWas macht eine bessere Konstante in C, ein Makro oder eine Aufzählung?

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

Privacy policy