it-swarm.com.de

"static const" vs "#define" vs "enum"

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

static const int var = 5;

oder 

#define var 5

oder

enum { var = 5 };
513
Vijay

Allgemein gesagt:

static const

Weil es den Geltungsbereich respektiert und typensicher ist.

Der einzige Nachteil, den 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 statt Makros/Ellipsis eine typsichere Alternative.

Wenn Sie wirklich ein Makro benötigen (z. B. __FILE__ oder __LINE__), sollten Sie das Makro SEHR sorgfältig benennen: In seiner NamenskonventionBoost empfiehlt alle Großbuchstaben. Beginnend mit dem Namen des Projekts (hier BOOST_) werden Sie beim Durchlesen der Bibliothek feststellen, dass dieser (in der Regel) der Name des jeweiligen Bereichs (Bibliothek) gefolgt von einem aussagekräftigen Namen folgt.

Es macht in der Regel für lange Namen :)

264
Matthieu M.

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

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

Fragen zur Wahl des Namens ignorieren, dann:

  • Wenn Sie einen Zeiger herumgeben müssen, müssen Sie (1) verwenden.
  • Da (2) offensichtlich eine Option ist, müssen Sie keine Zeiger herumlaufen lassen.
  • Sowohl (1) als auch (3) haben ein Symbol in der Symboltabelle des Debuggers - dies erleichtert das Debuggen. Es ist wahrscheinlicher, dass (2) kein Symbol hat, sodass Sie sich fragen, was es ist.
  • (1) kann nicht als Dimension für Arrays im globalen Geltungsbereich verwendet werden; sowohl (2) als auch (3) können.
  • (1) kann nicht als Dimension für statische Arrays im Funktionsumfang verwendet werden; sowohl (2) als auch (3) können.
  • Unter C99 können alle diese Werte für lokale Arrays verwendet werden. Technisch würde die Verwendung von (1) die Verwendung eines VLA (Array mit variabler Länge) bedeuten, obwohl die durch 'var' referenzierte Dimension natürlich auf Größe 5 festgelegt wäre.
  • (1) kann nicht an Orten wie switch-Anweisungen verwendet werden; sowohl (2) als auch (3) können.
  • (1) kann nicht zum Initialisieren statischer Variablen verwendet werden; sowohl (2) als auch (3) können.
  • (2) kann Code ändern, den Sie nicht ändern möchten, da er vom Präprozessor verwendet wird; Sowohl (1) als auch (3) haben keine unerwarteten Nebenwirkungen.
  • Sie können feststellen, ob (2) im Präprozessor eingestellt wurde. das lässt weder (1) noch (3) zu.

In den meisten Kontexten bevorzugen Sie also das 'Enum' den Alternativen. Ansonsten sind der erste und der letzte Punkt wahrscheinlich die Kontrollfaktoren - und Sie müssen stärker nachdenken, wenn Sie beide gleichzeitig befriedigen müssen.

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

620

In C speziell? In C lautet die richtige Antwort: Verwenden Sie #define (oder gegebenenfalls enum)

Obwohl es vorteilhaft ist, die Eigenschaften für das Scoping und die Typisierung eines const-Objekts zu haben, sind const-Objekte in C (im Gegensatz zu C++) keine echten Konstanten und daher in den meisten praktischen Fällen unbrauchbar.

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

Wenn dies für Sie wichtig ist, wird es Ihre Wahl bestimmen. Meistens haben Sie keine andere Wahl, als #define in C zu verwenden. Vergessen Sie nicht eine andere Alternative, die echte Konstanten in C - enum erzeugt.

In C++ sind const-Objekte echte Konstanten, daher ist es in C++ fast immer besser, die const-Variante zu bevorzugen (allerdings ist keine explizite static in C++ erforderlich).

100
AnT

Der Unterschied zwischen static const und #define besteht darin, dass ersterer den Speicher und der spätere Speicher nicht den Speicher verwendet. Zweitens können Sie die Adresse eines #define nicht übergeben, während Sie die Adresse eines static const übergeben können. Tatsächlich hängt es davon ab, unter welchen Umständen wir uns befinden, wir müssen einen von diesen beiden auswählen. Beide sind unter verschiedenen Umständen am besten. Bitte nicht davon ausgehen, dass einer besser ist als der andere ... :-)

Wenn das der Fall gewesen wäre, hätte Dennis Ritchie den besten alleine gelassen ... hahaha ... :-)

28
wrapperm

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

#define MAXLEN 5

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

ANSI C erlaubt in meinem Zusammenhang nicht, static consts zu verwenden, soweit ich weiß. In C++ sollten Sie in diesen Fällen Makros vermeiden. Du kannst schreiben

const int maxlen = 5;

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

und sogar static auslassen, da die interne Verknüpfung bereits von const impliziert wird [nur in C++].

14
sellibitze

Ein weiterer Nachteil von const in C ist, dass Sie den Wert nicht beim Initialisieren einer anderen const 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;

Selbst 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!

Gerne verwende ich in diesen Fällen const, ansonsten ...

13
Gauthier

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

Zumindest im ursprünglichen C-Standard ist es jedoch keine Konstante. Wenn Sie #define var 5 verwenden, können Sie int foo[var]; als Deklaration schreiben. Dies ist jedoch nicht möglich (außer als Compilererweiterung "mit static const int var = 5;". Dies ist in C++ nicht der Fall, wo die static const-Version überall dort verwendet werden kann, wo die #define-Version verwendet werden kann und ich glaube, dass dies auch bei C99 der Fall ist.

Benennen Sie niemals eine #define-Konstante mit einem Kleinbuchstaben. Es wird jede mögliche Verwendung dieses Namens bis zum Ende der Übersetzungseinheit außer Kraft setzen. Makrokonstanten sollten sich in dem eigentlich eigenen Namespace befinden, der traditionell aus Großbuchstaben besteht, möglicherweise mit einem Präfix. 

9
David Thornley

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

#include <stdio.h>

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);
}

Dies wird mit diesen Fehlern und Warnungen kompiliert:

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 ausgibt, wenn Definieren eine Warnung ausgibt.

6
Michael Potter

#define var 5 verursacht Probleme, wenn Sie Dinge wie mystruct.var haben.

Zum Beispiel,

struct mystruct {
    int var;
};

#define var 5

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

Der Präprozessor ersetzt es und der Code wird nicht kompiliert. Aus diesem Grund wird im traditionellen Codierungsstil empfohlen, dass alle Konstanten #defines Großbuchstaben verwenden, um Konflikte zu vermeiden.

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

Beispiel:

#define PI 3.1416

Der symbolische Name PI wird von Compilern möglicherweise nie gesehen. Es kann vom Präprozessor entfernt werden, bevor der Quellcode überhaupt einen Compiler erhält. 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 Konstanten erhalten, da sich die Fehlernachricht auf 3.1416 und nicht auf PI bezieht. Wenn PI in einer Header-Datei definiert wurde, die Sie nicht geschrieben haben, wissen Sie nicht, woher diese 3.1416-Datei 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...
5
suren

Die Definition

const int const_value = 5;

definiert nicht immer einen konstanten Wert. Einige Compiler (zum Beispiel tcc 0.9.26 ) weisen nur Speicherplatz zu, der mit dem Namen "const_value" gekennzeichnet ist. Mit dem Bezeichner "const_value" können Sie diesen Speicher nicht ändern. Sie können den Speicher jedoch noch mit einem anderen Bezeichner ä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

nur so lässt sich ein konstanter Wert definieren, der auf keine Weise verändert werden kann.

4
user2229691

Eine Alternative zu #define, die zwar den richtigen Bereich angibt, sich aber wie eine "echte" Konstante verhält, ist übrigens "enum". Zum Beispiel:

enum {number_ten = 10;}

In vielen Fällen ist es nützlich, aufgezählte Typen zu definieren und Variablen dieser Typen zu erstellen. Wenn dies erledigt ist, können Debugger möglicherweise Variablen nach ihrem Aufzählungsnamen anzeigen. 

Eine wichtige Einschränkung dabei ist jedoch: In C++ haben aufgezählte Typen eine eingeschränkte Kompatibilität mit ganzen Zahlen. Zum Beispiel kann man standardmäßig keine Arithmetik durchführen. Ich finde das ein merkwürdiges Standardverhalten für Aufzählungen; Es wäre zwar schön gewesen, einen "strengen Enum" -Typ zu haben, da C++ im Allgemeinen mit C kompatibel ist. Ich denke jedoch, dass das Standardverhalten eines "Enum" -Typs mit ganzen Zahlen austauschbar sein sollte.

3
supercat

Ich glaube nicht, dass es eine Antwort für "das ist immer am besten" gibt, aber wie Matthieu sagte 

static const

ist typsicher. Mein größter Liebling mit #define ist jedoch beim Debuggen in Visual Studio Sie können die Variable nicht beobachten. Es gibt einen Fehler, dass das Symbol nicht gefunden werden kann.

3
Afcrowe

Obwohl es sich bei der Frage um Ganzzahlen handelte, ist es erwähnenswert, dass #define und enums unbrauchbar sind, wenn Sie eine konstante Struktur oder einen String benötigen. Beide werden normalerweise als Funktionen an Funktionen übergeben. (Bei Strings ist es erforderlich; bei Strukturen ist es wesentlich 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 zur Laufzeit möglicherweise zwei consts hinzu, fügt jedoch zur Kompilierzeit zwei #defines hinzu. Eine # definierte Konstante kann in eine oder mehrere MOV-Anweisungen [unmittelbare] umgewandelt werden, was bedeutet, dass die Konstante effektiv im Programmspeicher gespeichert wird. Eine const-Konstante wird im Abschnitt .const im Datenspeicher gespeichert. In Systemen mit einer Harvard-Architektur können Unterschiede in der Leistung und in der Speicherauslastung auftreten, obwohl diese wahrscheinlich gering sind. Sie könnten für die Hardcore-Optimierung von inneren Schleifen von Bedeutung sein.

2
Adam Haun

Ein einfacher Unterschied:

Zum Zeitpunkt der Vorverarbeitung wird die Konstante durch ihren Wert .. ersetzt. Sie können den Dereferenzierungsoperator also nicht auf eine Definition anwenden, Sie können den Dereferenzierungsoperator jedoch auf eine Variable anwenden.

Definieren ist also schneller als das statische const.

Zum Beispiel mit:

#define mymax 100

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

Aber mit

const int mymax_var=100

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

Um genauer zu sein, wird das Define in der Vorverarbeitungsphase durch seinen Wert ersetzt, so dass im Programm keine Variablen gespeichert sind. Wir haben nur den Code aus dem Textsegment des Programms, in dem die Definition verwendet wurde.

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

Oben wollte ich über den Referenzoperator berichten, also die Referenzreferenz durch Referenz ersetzen.

1
mihaitzateo

Ich bin nicht sicher, ob ich Recht habe, aber meiner Meinung nach ist der Aufruf des #defined-Werts viel schneller als der Aufruf einer anderen normalerweise deklarierten Variablen (oder eines Konstantenwerts). Dies liegt daran, wenn das Programm ausgeführt wird und es eine normal deklarierte Variable benötigt muss an einen exakten Ort im Speicher springen, um diese Variable zu erhalten.

Im Gegensatz dazu muss das Programm bei Verwendung des #defined-Werts nicht in einen zugewiesenen Speicher springen, sondern nimmt nur den Wert an. Wenn #define myValue 7 und das Programm, das myValue aufruft, verhält es sich genauso wie beim Aufruf von 7.

0
pajczur

Wir haben uns den produzierten Assembler-Code auf dem MBF16X angesehen ... Beide Varianten ergeben denselben Code für arithmetische Operationen (z. B. ADD Immediate).

Daher wird const int für die Typenprüfung bevorzugt, während #define im alten Stil ist. Vielleicht ist es compilerspezifisch. Überprüfen Sie also Ihren produzierten Assembler-Code.

0
guest