Welches ist unter den folgenden Aussagen in C besser zu verwenden?
static const int var = 5;
oder
#define var 5
oder
enum { var = 5 };
Welches ist unter den folgenden Aussagen in C besser zu verwenden?
static const int var = 5;
oder
#define var 5
oder
enum { var = 5 };
Antworten:
Es hängt davon ab, wofür Sie den Wert benötigen. Sie (und alle anderen bisher) haben die dritte Alternative ausgelassen:
static const int var = 5;#define var 5enum { var = 5 };Ignorieren Sie Probleme bei der Wahl des Namens.
Ziehen Sie daher in den meisten Kontexten die Aufzählung den Alternativen vor. Andernfalls sind wahrscheinlich der erste und der letzte Aufzählungspunkt die bestimmenden Faktoren - und Sie müssen genauer überlegen, ob Sie beide gleichzeitig erfüllen müssen.
Wenn Sie nach C ++ fragen, verwenden Sie jedes Mal Option (1) - die statische Konstante.
enumist, dass sie als int([C99] 6.7.2.2/3) implementiert sind. Mit A #definekönnen Sie vorzeichenlose und lange mit Uund LSuffixe angeben und consteinen Typ angeben . enumkann Probleme mit üblichen Typkonvertierungen verursachen.
enumnoch #definezusätzlichen Platz an sich verbraucht. Der Wert wird im Objektcode als Teil der Anweisungen angezeigt und nicht als Speicher im Datensegment, im Heap oder auf dem Stapel zugewiesen. Sie haben etwas Speicherplatz für den static const int, aber der Compiler kann ihn möglicherweise optimieren, wenn Sie keine Adresse angeben.
enums (und static const): Sie können nicht geändert werden. a definekann #undefine'd sein, wobei an enumund static constauf den angegebenen Wert festgelegt sind.
Allgemein gesagt:
static const
Weil es den Umfang 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 / Auslassungspunkten eine typsichere Alternative.
Wenn Sie wirklich mit einem Makro arbeiten müssen (zum Beispiel möchten __FILE__oder __LINE__), sollten Sie Ihr Makro SEHR sorgfältig benennen : In seiner Namenskonvention empfiehlt Boost alle Großbuchstaben, beginnend mit dem Namen des Projekts (hier BOOST_ ), während Sie die Bibliothek durchsuchen, werden Sie feststellen, dass (im Allgemeinen) der Name des bestimmten Bereichs (der Bibliothek) gefolgt von einem aussagekräftigen Namen folgt.
Es macht in der Regel für lange Namen :)
staticübrig bleiben, deren Adresse übernommen wird. und wenn die Adresse genommen wird, hätte man kein #defineoder enum(keine Adresse) verwenden können ... also sehe ich wirklich nicht, welche Alternative hätte verwendet werden können. Wenn Sie die "Auswertung der Kompilierungszeit" abschaffen können, suchen Sie möglicherweise extern conststattdessen nach.
#ifkönnte über vorzuziehen sein , #ifdeffür boolean Flaggen, aber in diesem Fall wäre es unmöglich machen zu definieren , varwie 0von der Kommandozeile. Also in diesem Fall #ifdefsinnvoller, solange 0ein rechtlicher Wert für var.
Speziell in C? In C lautet die richtige Antwort: verwenden #define(oder gegebenenfalls)enum )
Während es constin der Realität vorteilhaft ist, die Scoping- und Typisierungseigenschaften eines Objekts zu habenconst Objekte in C (im Gegensatz zu C ++) keine echten Konstanten und daher in den meisten praktischen Fällen normalerweise unbrauchbar.
In C sollte die Auswahl also davon abhängen, wie Sie Ihre Konstante verwenden möchten. Beispielsweise können Sie ein const intObjekt nicht als caseBeschriftung verwenden (solange ein Makro funktioniert). Sie können ein const intObjekt nicht als Bitfeldbreite verwenden (während ein Makro funktioniert). In C89 / 90 können Sie kein constObjekt verwenden, um eine Arraygröße anzugeben (während ein Makro funktioniert). Selbst in C99 können Sie kein constObjekt verwenden, um eine Arraygröße anzugeben, wenn Sie ein Nicht- VLA- Array benötigen .
Wenn dies für Sie wichtig ist, bestimmt es Ihre Wahl. Meistens haben Sie keine andere Wahl, als #definein C zu verwenden . Und vergessen Sie nicht eine andere Alternative, die echte Konstanten in C - erzeugt enum.
In C ++ sind constObjekte echte Konstanten, daher ist es in C ++ fast immer besser, die constVariante zu bevorzugen ( in C ++ ist dies jedoch nicht explizit erforderlich static).
const intObjekten in Fallbezeichnungen ist in allen Versionen der C-Sprache unzulässig. (Natürlich kann Ihr Compiler ihn als nicht standardmäßige C ++ - ähnliche Spracherweiterung unterstützen.)
constbedeutet schreibgeschützt. const int r = rand();ist vollkommen legal.
constexprals constspeziell mit den stlContainern wie arrayoder bitset.
switch()Aussage getestet haben , nicht in caseeiner. Ich bin auch gerade dabei gewesen caught
Der Unterschied zwischen static constund #definebesteht darin, dass der erstere den Speicher verwendet und der letztere den Speicher nicht zum Speichern verwendet. Zweitens können Sie die Adresse von a nicht übergeben, #definewährend Sie die Adresse von a übergeben könnenstatic const . Je nachdem, unter welchen Umständen wir uns befinden, müssen wir einen dieser beiden auswählen. Beide sind unter verschiedenen Umständen von ihrer besten Seite. Bitte nimm nicht an, dass einer besser ist als der andere ... :-)
Wenn das der Fall gewesen wäre, hätte Dennis Ritchie den Besten alleine gelassen ... hahaha ... :-)
constSpeicher verwendet. GCC (getestet mit 4.5.3 und einigen neueren Versionen) optimiert const intbei Verwendung von -O3 problemlos das direkte Literal in Ihrem Code. Wenn Sie also eine eingebettete Entwicklung mit wenig RAM (z. B. AVR) durchführen, können Sie C-Konstanten sicher verwenden, wenn Sie GCC oder einen anderen kompatiblen Compiler verwenden. Ich habe es nicht getestet, erwarte aber, dass Clang übrigens dasselbe tut.
In C #defineist viel beliebter. Sie können diese Werte zum Deklarieren von Arraygrößen verwenden, zum Beispiel:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C erlaubt Ihnen meines Wissens nicht, static consts in diesem Zusammenhang 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 sogar weglassen, staticweil die interne Verknüpfung constbereits von [nur in C ++] impliziert wird .
const int MY_CONSTANT = 5;in einer Datei haben und in einer extern const int MY_CONSTANT;anderen darauf zugreifen . Ich konnte im Standard (mindestens C99) keine Informationen zum constÄndern des Standardverhaltens finden. "6.2.2: 5 Wenn die Deklaration eines Bezeichners für ein Objekt einen Dateibereich und keinen Speicherklassenspezifizierer hat, ist seine Verknüpfung extern."
barist ein VLA (Array variabler Länge); Der Compiler generiert wahrscheinlich Code, als ob seine Länge konstant wäre.
Ein weiterer Nachteil von constC ist, dass Sie den Wert nicht zum Initialisieren eines anderen verwenden können const.
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 dies funktioniert nicht mit einer Konstante, da der Compiler sie nicht als Konstante betrachtet:
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 constin diesen Fällen getippt verwenden, sonst ...
static uint8_t const ARRAY_SIZE = 16;kann eine Herausforderung sein, herauszufinden, warum Sie plötzlich nicht mehr kompiliert werden, insbesondere wenn die #define ARRAY_SIZE 256zehn Ebenen tief in einem Wirrwarr von Headern vergraben sind. Der Name aller Großbuchstaben ARRAY_SIZEbittet um Ärger. Reservieren Sie ALL_CAPS für Makros und definieren Sie niemals ein Makro, das nicht in ALL_CAPS-Form vorliegt.
const. Dies könnte mehr positiv bewertet werden!
Wenn Sie damit durchkommen können, static consthat das viele Vorteile. Es folgt den normalen Gültigkeitsbereichsprinzipien, ist in einem Debugger sichtbar und befolgt im Allgemeinen die Regeln, denen Variablen folgen.
Zumindest im ursprünglichen C-Standard ist dies jedoch keine Konstante. Wenn Sie verwenden #define var 5, können Sie int foo[var];als Deklaration schreiben , aber das können Sie nicht (außer als Compiler-Erweiterung "mit static const int var = 5;. Dies ist in C ++ nicht der Fall, wo die static constVersion überall verwendet werden #definekann, wo die Version kann, und ich glaube das ist auch bei C99 der Fall.
Benennen Sie jedoch niemals eine #defineKonstante mit einem Kleinbuchstaben. Jede mögliche Verwendung dieses Namens wird bis zum Ende der Übersetzungseinheit außer Kraft gesetzt. Makrokonstanten sollten sich in einem eigenen Namespace befinden, der traditionell nur aus Großbuchstaben besteht, möglicherweise mit einem Präfix.
constin C99 ist immer noch keine echte Konstante. Sie können die Arraygröße mit a constin C99 deklarieren , jedoch nur, weil C99 Arrays mit variabler Länge unterstützt. Aus diesem Grund funktioniert es nur, wenn VLAs zulässig sind. Selbst in C99 können Sie beispielsweise a nicht verwenden, constum die Größe eines Member-Arrays in a zu deklarieren struct.
const intGröße perfekt initialisieren, als wäre es eine C ++ - Konstante oder ein Makro. Ob Sie sich auf diese Abweichung von GCC vom Standard verlassen möchten, ist natürlich Ihre Wahl. Ich persönlich würde mich dafür entscheiden, es sei denn, Sie können wirklich die Verwendung eines anderen Compilers als GCC oder Clang vorhersehen. Letzterer hat hier die gleiche Funktion (getestet mit Clang) 3.7).
Es ist IMMER vorzuziehen, const anstelle von #define zu verwenden. Dies liegt daran, dass const vom Compiler und #define vom Präprozessor behandelt wird. Es ist, 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 zu einem Compiler gelangt. Infolgedessen wird der Name PI möglicherweise nicht in die Symboltabelle eingegeben. Dies kann verwirrend sein, wenn beim Kompilieren ein Fehler auftritt, bei dem die Konstante verwendet wird, da sich die Fehlermeldung möglicherweise auf 3.1416 und nicht auf PI bezieht. Wenn PI in einer Header-Datei definiert wäre, die Sie nicht geschrieben haben, hätten 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...
#define var 5wird Ihnen Probleme bereiten, wenn Sie Dinge wie haben mystruct.var.
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 schlägt der traditionelle Codierungsstil vor, dass alle Konstanten #defineGroßbuchstaben verwenden, um Konflikte zu vermeiden.
Ich habe ein schnelles Testprogramm geschrieben, um einen Unterschied zu demonstrieren:
#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 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 ausgibt, wenn define eine Warnung ausgibt.
Die Definition
const int const_value = 5;
definiert nicht immer einen konstanten Wert. Einige Compiler (z. B. tcc 0.9.26 ) weisen nur Speicher 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 weiterhin 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
Nur so kann ein konstanter Wert definiert werden, der auf keinen Fall geändert werden kann.
#definekönnen Sie dies auch ändern, indem Sie den Maschinencode bearbeiten.
5. Aber man kann nicht ändern, #defineweil es ein Präprozessor-Makro ist. Es existiert nicht im Binärprogramm. Wenn man alle Stellen ändern wollte, an denen sie CONST_VALUEverwendet wurden, musste man dies einzeln tun.
#define CONST 5, if (CONST == 5) { do_this(); } else { do_that(); }und der Compiler entfernt den elseZweig. Wie schlagen Sie vor, den Maschinencode zu bearbeiten, um ihn CONSTauf 6 zu ändern ?
#defineist nicht kugelsicher.
#define. Der einzige wirkliche Weg, dies zu tun, besteht darin, den Quellcode zu bearbeiten und neu zu kompilieren.
Obwohl es sich um Ganzzahlen handelte, ist es erwähnenswert, dass #define und enums nutzlos sind, wenn Sie eine konstante Struktur oder Zeichenfolge benötigen. Diese werden normalerweise als Zeiger an Funktionen übergeben. (Mit Strings ist es erforderlich; mit Strukturen ist es viel effizienter.)
Bei Ganzzahlen müssen Sie sich in einer eingebetteten Umgebung mit sehr begrenztem Speicher möglicherweise Gedanken darüber machen, wo die Konstante gespeichert ist und wie die Zugriffe darauf kompiliert werden. Der Compiler fügt zur Laufzeit möglicherweise zwei Konstanten hinzu, zur Kompilierungszeit jedoch zwei #defines. Eine # define-Konstante kann in eine oder mehrere MOV-Anweisungen (sofort) konvertiert werden, was bedeutet, dass die Konstante effektiv im Programmspeicher gespeichert ist. Eine const-Konstante wird im Abschnitt .const im Datenspeicher gespeichert. In Systemen mit einer Harvard-Architektur kann es zu Unterschieden in der Leistung und der Speichernutzung kommen, obwohl diese wahrscheinlich gering sind. Sie könnten für die Hardcore-Optimierung innerer Schleifen von Bedeutung sein.
Denken Sie nicht, dass es eine Antwort für "was immer am besten ist" gibt, aber wie Matthieu sagte
static const
ist typsicher. Mein größter Ärger mit Haustieren #defineist jedoch, dass Sie beim Debuggen in Visual Studio die Variable nicht sehen können. Es gibt einen Fehler, dass das Symbol nicht gefunden werden kann.
Übrigens #defineist "enum" eine Alternative zu , die ein angemessenes Scoping bietet, sich aber wie eine "echte" Konstante verhält. Zum Beispiel:
enum {number_ten = 10;}
In vielen Fällen ist es hilfreich, Aufzählungstypen zu definieren und Variablen dieser Typen zu erstellen. In diesem Fall können Debugger möglicherweise Variablen entsprechend ihrem Aufzählungsnamen anzeigen.
Eine wichtige Einschränkung dabei: In C ++ sind Aufzählungstypen nur eingeschränkt mit Ganzzahlen kompatibel. Zum Beispiel kann man standardmäßig keine Arithmetik für sie durchführen. Ich finde, dass dies ein merkwürdiges Standardverhalten für Aufzählungen ist. Während es schön gewesen wäre, einen "strengen Aufzählungstyp" zu haben, würde ich denken, dass das Standardverhalten eines "Aufzählungstyps" mit ganzen Zahlen austauschbar sein sollte, da C ++ im Allgemeinen mit C kompatibel sein soll.
int, daher kann der "Enum-Hack" nicht mit anderen Ganzzahltypen verwendet werden. (Die Aufzählung Typ ist kompatibel mit einiger Implementierung definiert Integer - Typ, nicht unbedingt int, aber in diesem Fall ist der Typ ist anonym , so dass spielt keine Rolle.)
inteine Variable vom Typ Aufzählung zuweist (was Compiler tun dürfen) und man versucht, einer solchen Variablen zuzuweisen ein Mitglied seiner eigenen Aufzählung. Ich wünschte, Standardkomitees würden tragbare Methoden zum Deklarieren von Ganzzahltypen mit spezifizierter Semantik hinzufügen. JEDE Plattform sollte unabhängig von ihrer charGröße in der Lage sein, z. B. einen Typ zu deklarieren, der den Mod 65536 umschließt, selbst wenn der Compiler viele AND R0,#0xFFFFoder gleichwertige Anweisungen hinzufügen muss .
uint16_t, obwohl dies natürlich kein Aufzählungstyp ist. Es wäre schön, wenn der Benutzer den Ganzzahltyp angeben würde, der zur Darstellung eines bestimmten Aufzählungstyps verwendet wird, aber Sie können mit einem typedeffor uint16_tund einer Reihe von #defines für einzelne Werte weitgehend den gleichen Effekt erzielen .
2U < -1Lals wahr und andere als falsch bewertet werden , und wir bleiben jetzt bei der Tatsache, dass einige Plattformen einen Vergleich zwischen uint32_tund int32_tals signiert implementieren und einige als nicht signiert, aber das bedeutet nicht, dass das Komitee keinen aufwärtskompatiblen Nachfolger für C definieren konnte, der Typen enthält, deren Semantik auf allen Compilern konsistent wäre.
Ein einfacher Unterschied:
Zur Vorverarbeitungszeit wird die Konstante durch ihren Wert ersetzt. Sie können den Dereferenzierungsoperator also nicht auf eine Definition anwenden, aber Sie können den Dereferenzierungsoperator auf eine Variable anwenden.
Wie Sie annehmen würden, ist define schneller als statische const.
Zum Beispiel mit:
#define mymax 100
du kannst nicht tun printf("address of constant is %p",&mymax);.
Aber mit
const int mymax_var=100
du kannst tun printf("address of constant is %p",&mymax_var);.
Um es klarer zu machen, wird die Definition in der Vorverarbeitungsphase durch ihren Wert ersetzt, sodass keine Variable im Programm gespeichert ist. Wir haben nur den Code aus dem Textsegment des Programms, in dem die Definition verwendet wurde.
Für statische Konstanten haben wir jedoch eine Variable, die irgendwo zugeordnet ist. Für gcc werden statische Konstanten im Textsegment des Programms zugewiesen.
Oben wollte ich etwas über den Referenzoperator erzählen, also ersetze die Dereferenzierung durch die Referenz.
constQualifikationsmerkmal hat. C hat keine anderen Symbolikkonstanten als Enum-Konstanten . A const intist eine Variable. Sie verwechseln auch die Sprache und bestimmte Implementierungen. Es ist nicht erforderlich, wo das Objekt platziert werden soll. Und es gilt nicht einmal für gcc: Normalerweise werden constqualifizierte Variablen in den .rodataAbschnitt eingefügt. Das hängt aber von der Zielplattform ab. Und Sie meinen die Adresse des Betreibers &.
Wir haben uns den erzeugten Assembler-Code auf dem MBF16X angesehen ... Beide Varianten führen zu demselben Code für arithmetische Operationen (z. B. ADD Immediate).
Wird const intalso für die Typprüfung bevorzugt, während #definees sich um einen alten Stil handelt. Vielleicht ist es compilerspezifisch. Überprüfen Sie also Ihren erstellten Assembler-Code.
Ich bin mir nicht sicher, ob ich Recht habe, aber meiner Meinung nach rufe ich an #define d value viel schneller als das Aufrufen einer anderen normalerweise deklarierten Variablen (oder eines konstanten Werts). Dies liegt daran, dass das Programm, wenn es ausgeführt wird und eine normalerweise deklarierte Variable verwenden muss, an die genaue Stelle im Speicher springen muss, um diese Variable abzurufen.
Im Gegensatz dazu #definemuss das Programm, wenn es den Wert d verwendet, nicht zu einem zugewiesenen Speicher springen, sondern nimmt nur den Wert. Wenn #define myValue 7und das Programm aufrufen myValue, verhält es sich genauso wie beim Aufrufen 7.