“Statisch const” vs “#define” vs “enum”

Welche ist besser unter den folgenden Aussagen in C zu verwenden?

static const int var = 5; 

oder

 #define var 5 

oder

 enum { var = 5 }; 

Solutions Collecting From Web of "“Statisch const” vs “#define” vs “enum”"

Allgemein gesagt:

 static const 

Weil es scope respektiert und typsicher ist.

Die einzige Einschränkung, die ich sehen konnte: Wenn Sie möchten, dass die Variable möglicherweise in der Befehlszeile definiert wird. Es gibt noch eine Alternative:

 #ifdef VAR // Very bad name, not long enough, too general, etc.. static int const var = VAR; #else static int const var = 5; // default value #endif 

Verwenden Sie nach Möglichkeit anstelle von Makros / Ellipsen eine typsichere Alternative.

Wenn Sie wirklich mit einem Makro gehen müssen (zum Beispiel wollen Sie __FILE__ oder __LINE__ ), dann sollten Sie Ihr Makro SEHR sorgfältig benennen: Boost empfiehlt in seiner Namenskonvention alle Großbuchstaben, beginnend mit dem Namen des Projekts ( hier BOOST_), während Sie die Bibliothek durchlesen, werden Sie bemerken, dass dies (allgemein) gefolgt wird von dem Namen des bestimmten Bereichs (Bibliothek), dann mit einem aussagekräftigen Namen.

Es macht im Allgemeinen lange Namen 🙂

Es hängt davon ab, wofür Sie den Wert benötigen. Sie (und alle anderen bis jetzt) ​​haben die dritte Alternative weggelassen:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignoriere Probleme bezüglich der Wahl des Namens, dann:

  • Wenn Sie einen pointers übergeben müssen, müssen Sie (1) verwenden.
  • Da (2) offensichtlich eine Option ist, müssen Sie keine pointers weitergeben.
  • Sowohl (1) als auch (3) haben ein Symbol in der Symboltabelle des Debuggers – das erleichtert das Debuggen. Es ist wahrscheinlicher, dass (2) kein Symbol hat und Sie sich fragen, was es ist.
  • (1) kann nicht als Dimension für Arrays im globalen Gültigkeitsbereich verwendet werden; beide (2) und (3) können.
  • (1) kann nicht als Dimension für statische Arrays im functionsumfang verwendet werden; beide (2) und (3) können.
  • Alle diese können unter C99 für lokale Arrays verwendet werden. Technisch würde die Verwendung von (1) die Verwendung eines VLA (Array variabler Länge) implizieren, obwohl die durch ‘var’ referenzierte Dimension natürlich auf Größe 5 festgelegt wäre.
  • (1) kann nicht an Orten wie Switch-statementen verwendet werden; beide (2) und (3) können.
  • (1) kann nicht verwendet werden, um statische Variablen zu initialisieren; beide (2) und (3) können.
  • (2) kann Code ändern, den Sie nicht ändern wollten, weil er vom Präprozessor verwendet wird; Sowohl (1) als auch (3) werden keine unerwarteten Nebenwirkungen wie diese haben.
  • Sie können erkennen, ob (2) im Präprozessor gesetzt wurde; weder (1) noch (3) erlaubt dies.

Also, in den meisten Kontexten, bevorzugen Sie die “enum” über die Alternativen. Andernfalls sind die ersten und letzten Aufzählungspunkte wahrscheinlich die steuernden Faktoren – und Sie müssen härter denken, wenn Sie beides gleichzeitig erfüllen müssen.

Wenn Sie nach C ++ fragen würden, würden Sie jedes Mal die Option (1) – die statische Konstante – verwenden.

In C speziell? In C lautet die richtige Antwort: benutze #define (oder ggf. enum )

Während es vorteilhaft ist, die Eigenschaften eines const Objekts zu definieren, sind in der Realität const Objekte in C (im Gegensatz zu C ++) keine wahren Konstanten und daher in den meisten praktischen Fällen in der Regel unbrauchbar.

In C sollte die Entscheidung daher davon abhängen, wie Sie Ihre Konstante verwenden möchten. Beispielsweise können Sie ein const int Objekt nicht als case Label verwenden (während ein Makro funktioniert). Sie können ein const int Objekt nicht als const int (während ein Makro funktioniert). In C89 / 90 können Sie kein const Objekt verwenden, um eine Array-Größe anzugeben (während ein Makro funktioniert). Selbst in C99 können Sie kein const Objekt verwenden, um eine Array-Größe anzugeben, wenn Sie ein Nicht- VLA- Array benötigen.

Wenn dies für Sie wichtig ist, wird es Ihre Wahl bestimmen. Die meiste Zeit haben Sie keine andere Wahl, als #define in C zu verwenden. Und vergessen Sie nicht eine andere Alternative, die wahre Konstanten in C – enum .

In C ++ sind konstante Objekte wahre Konstanten, so dass es in C ++ fast immer besser ist, die const Variante vorzuziehen (keine Notwendigkeit für explizites static in C ++).

Der Unterschied zwischen static const und #define besteht darin, dass der erste den Speicher verwendet und der spätere den Speicher nicht zum Speichern verwendet. Zweitens können Sie die Adresse eines #define während Sie die Adresse eines static const . Eigentlich hängt es davon ab, unter welchen Umständen wir uns befinden, wir müssen einen von diesen beiden auswählen. Beide sind unter anderen Umständen am besten. Bitte gehen Sie nicht davon aus, dass einer besser ist als der andere … 🙂

Wenn das der Fall gewesen wäre, hätte Dennis Ritchie den Besten allein gelassen … hahaha … 🙂

In C #define ist viel beliebter. Sie können diese Werte zum Deklarieren von Array-Größen verwenden, zum Beispiel:

 #define MAXLEN 5 void foo(void) { int bar[MAXLEN]; } 

ANSI C erlaubt in diesem Zusammenhang static const nicht, static const zu verwenden. In C ++ sollten Sie in diesen Fällen Makros vermeiden. Du kannst schreiben

 const int maxlen = 5; void foo() { int bar[maxlen]; } 

und lassen Sie sogar static weil die interne Verknüpfung von const bereits [nur in C ++] impliziert wird.

Ein weiterer Nachteil von const in C ist, dass Sie den Wert beim Initialisieren eines anderen const nicht verwenden können.

 static int const NUMBER_OF_FINGERS_PER_HAND = 5; static int const NUMBER_OF_HANDS = 2; // initializer element is not constant, this does not work. static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND * NUMBER_OF_HANDS; 

Auch das funktioniert nicht mit einem const, da der Compiler es nicht als Konstante sieht:

 static uint8_t const ARRAY_SIZE = 16; static int8_t const lookup_table[ARRAY_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant! 

Ich würde gerne typisierte const in diesen Fällen verwenden, sonst …

Wenn Sie damit durchkommen können, hat static const viele Vorteile. Es gehorcht den Prinzipien des normalen Bereichs, ist in einem Debugger sichtbar und gehorcht im Allgemeinen den Regeln, denen die Variablen folgen.

Zumindest im ursprünglichen C-Standard ist es jedoch keine Konstante. Wenn Sie #define var 5 , können Sie int foo[var]; als Deklaration, aber das kann man nicht machen (außer als Compiler-Erweiterung “mit static const int var = 5; Dies ist in C ++ nicht der Fall, wobei die static const Version überall dort verwendet werden kann, wo die # #define Version und ich glaube, das ist auch bei C99 der Fall.

#define jedoch niemals eine #define Konstante mit einem Kleinbuchstaben. Es wird jede mögliche Verwendung dieses Namens bis zum Ende der Übersetzungseinheit überschreiben. Makro-Konstanten sollten sich in einem eigenen Namensraum befinden, der traditionell aus Großbuchstaben besteht, vielleicht mit einem Präfix.

#define var 5 wird Ihnen Probleme bereiten, wenn Sie Dinge wie mystruct.var .

Beispielsweise,

 struct mystruct { int var; }; #define var 5 int main() { struct mystruct foo; foo.var = 1; return 0; } 

Der Präprozessor wird es ersetzen und der Code wird nicht kompiliert. Aus diesem Grund schlägt der traditionelle Codierungsstil vor, dass alle konstanten #define s Großbuchstaben verwenden, um Konflikte zu vermeiden.

Ich habe ein schnelles Testprogramm geschrieben, um einen Unterschied zu demonstrieren:

 #include  enum {ENUM_DEFINED=16}; enum {ENUM_DEFINED=32}; #define DEFINED_DEFINED 16 #define DEFINED_DEFINED 32 int main(int argc, char *argv[]) { printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED); return(0); } 

Das kompiliert mit diesen Fehlern und Warnungen:

 main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED' enum {ENUM_DEFINED=32}; ^ main.c:5:7: note: previous definition is here enum {ENUM_DEFINED=16}; ^ main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined] #define DEFINED_DEFINED 32 ^ main.c:8:9: note: previous definition is here #define DEFINED_DEFINED 16 ^ 

Beachten Sie, dass enum einen Fehler gibt, wenn define eine Warnung gibt.

Die Definition

 const int const_value = 5; 

definiert nicht immer einen konstanten Wert. Einige Compiler (z. B. tcc 0.9.26 ) reservieren nur Speicher mit dem Namen “const_value”. Mit dem Bezeichner “const_value” können Sie diesen Speicher nicht ändern. Aber Sie könnten den Speicher immer noch mit einer anderen Kennung ändern:

 const int const_value = 5; int *mutable_value = (int*) &const_value; *mutable_value = 3; printf("%i", const_value); // The output may be 5 or 3, depending on the compiler. 

Dies bedeutet die Definition

 #define CONST_VALUE 5 

ist die einzige Möglichkeit, einen konstanten Wert zu definieren, der auf keine Weise verändert werden kann.

Es ist IMMER vorzuziehen, const anstelle von #define zu verwenden. Das liegt daran, dass const vom Compiler und #define vom Präprozessor behandelt wird. Es ist wie #define selbst ist nicht Teil des Codes (grob gesagt).

Beispiel:

 #define PI 3.1416 

Der symbolische Name PI darf niemals von Compilern gesehen werden; Es kann vom Präprozessor entfernt werden, bevor der Quellcode überhaupt zu einem Compiler gelangt. Daher wird der Name PI möglicherweise nicht in die Symboltabelle eingetragen. Dies kann verwirrend sein, wenn Sie beim Kompilieren einen Fehler mit der Verwendung der Konstante erhalten, da sich die Fehlermeldung möglicherweise auf 3.1416 bezieht, nicht auf PI. Wenn PI in einer Header-Datei definiert wurde, die Sie nicht geschrieben haben, haben Sie keine Ahnung, woher diese 3.1416 stammt.

Dieses Problem kann auch in einem symbolischen Debugger auftreten, da der Name, mit dem Sie programmieren, möglicherweise nicht in der Symboltabelle enthalten ist.

Lösung:

 const double PI = 3.1416; //or static const... 

Glauben Sie nicht, dass es eine Antwort für “das ist immer das Beste” gibt, wie Matthieu sagte

static const

ist typsicher. Mein größtes Ärgernis mit #define ist jedoch, dass man beim Debuggen in Visual Studio die Variable nicht sehen kann. Es gibt einen Fehler, dass das Symbol nicht gefunden werden kann.

Übrigens ist eine Alternative zu #define , die eine korrekte Bereichsdefinition bereitstellt, sich aber wie eine “echte” Konstante verhält, “enum”. Beispielsweise:

 enum {number_ten = 10;} 

In vielen Fällen ist es sinnvoll, Aufzählungstypen zu definieren und Variablen dieser Typen zu erstellen. Wenn dies gemacht wird, können Debugger möglicherweise Variablen entsprechend ihrem Aufzählungsnamen anzeigen.

Ein wichtiger Vorbehalt dabei: In C ++ haben Aufzählungstypen eine eingeschränkte Kompatibilität mit Ganzzahlen. Zum Beispiel kann man sie standardmäßig nicht arithmetisch ausführen. Ich finde das ein merkwürdiges Standardverhalten für Enums; Während es schön gewesen wäre, einen “strict enum” -Typ zu haben, angesichts des Wunsches, C ++ im Allgemeinen mit C kompatibel zu machen, würde ich denken, dass das Standardverhalten eines “enum” -Typs mit Ganzzahlen austauschbar sein sollte.

Obwohl sich die Frage auf Ganzzahlen bezog, ist es bemerkenswert, dass #define und enums nutzlos sind, wenn Sie eine konstante Struktur oder einen String benötigen. Diese werden normalerweise an functionen als pointers übergeben. (Mit Strings ist es erforderlich; mit Strukturen ist es viel effizienter.)

Wenn Sie sich in einer eingebetteten Umgebung mit sehr begrenztem Speicher befinden, müssen Sie sich möglicherweise Gedanken darüber machen, wo die Konstante gespeichert ist und wie Zugriffe darauf kompiliert werden. Der Compiler fügt möglicherweise zur Laufzeit zwei Fehler hinzu, fügt jedoch zwei #defines zur Kompilierzeit hinzu. Eine #define-Konstante kann in eine oder mehrere MOV [instant] -statementen umgewandelt werden, was bedeutet, dass die Konstante effektiv im Programmspeicher gespeichert wird. Eine konstante Konstante wird im .const-Abschnitt im Datenspeicher gespeichert. In Systemen mit einer Harvard-Architektur kann es Unterschiede in der performance und der Speichernutzung geben, obwohl sie wahrscheinlich klein sind. Sie könnten für die Optimierung innerer Schleifen wichtig sein.

Ein einfacher Unterschied:

Zur Vorverarbeitungszeit wird die Konstante durch ihren Wert ersetzt. Sie konnten also den Dereferenzoperator nicht auf eine Definition anwenden, aber Sie können den Dereferenzoperator auf eine Variable anwenden.

Wie Sie annehmen würden, ist define schneller als statisches const.

Zum Beispiel mit:

 #define mymax 100 

Sie können printf("address of constant is %p",&mymax); nicht printf("address of constant is %p",&mymax); .

Aber haben

 const int mymax_var=100 

Sie können printf("address of constant is %p",&mymax_var); .

Um es noch deutlicher zu machen, das Define wird in der Pre-Processing-Phase durch seinen Wert ersetzt, so dass keine Variablen im Programm gespeichert sind. Wir haben nur den Code aus dem Textsegment des Programms, in dem das Define verwendet wurde.

Für statische Konstanten haben wir jedoch eine Variable, die irgendwo zugewiesen ist. Für gcc wird static const im Textsegment des Programms zugewiesen.

Oben wollte ich über den Referenzoperator erzählen, also die Dereferenz mit Referenz ersetzen.

Wir haben uns den erzeugten Assembler-Code auf dem MBF16X angeschaut … Beide Varianten ergeben denselben Code für arithmetische Operationen (zB ADD Immediate).

Daher wird const int für die #define bevorzugt, während #define ein alter Stil ist. Vielleicht ist es Compiler-spezifisch. Überprüfe also deinen produzierten Assembler Code.