Interessanterweise ist dies fast genau die gleiche Frage wie stackoverflow.com/questions/1637332/static-const-vs-define . Der einzige Unterschied besteht darin, dass es sich bei dieser Frage um C ++ handelt und bei dieser um C. Da meine Antwort C ++ -spezifisch war, sage ich, dass sie nicht identisch sind, andere jedoch möglicherweise nicht übereinstimmen.
TED
53
Auf jeden Fall nicht identisch. Es gibt viele Bereiche, in denen C ++ aus Kompatibilitätsgründen die C-Syntax zulässt. In diesen Fällen haben Fragen wie "Was ist der beste Weg, um X zu tun?" In C ++ unterschiedliche Antworten. ZB Objektinitialisierung.
Wie ist das nicht meinungsbasiert? Sie haben jeweils einen anderen Zweck
Sam Hammamy
1
@ RobertSsupportsMonicaCellio, ja. Vielen Dank für die Andeutung
Vijay
Antworten:
690
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 5
enum { var = 5 };
Ignorieren Sie Probleme bei der Wahl des Namens.
Wenn Sie einen Zeiger herumgeben müssen, müssen Sie (1) verwenden.
Da (2) anscheinend eine Option ist, müssen Sie keine Zeiger 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, sodass Sie sich fragen, was es ist.
(1) kann nicht als Dimension für Arrays im globalen Bereich 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 Elemente 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-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, weil er vom Präprozessor verwendet wird; Sowohl (1) als auch (3) haben keine solchen unerwarteten Nebenwirkungen.
Sie können feststellen, ob (2) im Präprozessor eingestellt wurde. weder (1) noch (3) erlauben dies.
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.
fantastische Liste! Ein Nachteil 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.
Gauthier
37
(2) Personen beschweren sich IMMER über die Typensicherheit. Ich verstehe nie, warum man nicht einfach "#define var ((int) 5)" verwendet und Hurra, Sie haben Typensicherheit mit einer Definition.
Ingo Blackman
6
@ RedX: Sie müssten sich in einer sehr eigenartigen Umgebung befinden, um Platz für ein Unternehmen zu schaffen. Das heißt, weder 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.
Jonathan Leffler
15
Noch eine 'Stimme' für 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.
Daan Timmer
15
@ QED: Nein, danke. Eine einfache Konstante ist außerhalb von Klammern sicher. Oder zeigen Sie mir, wie ein Programm, von dem zu Recht erwartet wird, dass es kompiliert wird, geändert wird, wenn die 5 nicht in Klammern steht. Wenn es das Argument für ein Makro im Funktionsstil war oder wenn der Ausdruck Operatoren enthielt, wäre es richtig, mir die Schuld zu geben, wenn ich die Klammern nicht eingefügt hätte. Das ist hier aber nicht der Fall.
Jonathan Leffler
282
Allgemein gesagt:
staticconst
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..staticintconst var = VAR;#elsestaticintconst 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.
Einverstanden - auch bei #define besteht die allgemeine Gefahr, dass Code beschädigt wird, da der Präprozessor die Syntax nicht kennt.
NeilDurant
10
Es ist besser, #if als #ifdef zu verwenden, aber ansonsten stimme ich zu. +1.
Tim Post
58
Dies ist Standard-C ++ - Evangelisation. Die Antwort unten ist VIEL klarer, wenn es darum geht, zu erklären, was die Optionen wirklich sind und bedeuten. Insbesondere: Ich hatte gerade ein Problem mit "static const". Jemand hat damit etwa 2000 "Konstanten" in einer Header-Datei definiert. Dann wurde diese Header-Datei in ungefähr 100 ".c" - und ".cpp" -Dateien aufgenommen. => 8 MB für "consts". Großartig. Ja, ich weiß, dass Sie möglicherweise einen Linker verwenden, um nicht referenzierte Konstanten zu entfernen, aber dann bleiben Ihnen immer noch die "Konstanten", auf die verwiesen wird. Der Platz geht zur Neige, was an dieser Antwort falsch ist.
Ingo Blackman
2
@IngoBlackman: Mit einem guten Compiler sollten nur diejenigen 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.
Matthieu M.
15
@ Tim Beitrag #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.
Maarten
108
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).
"Sie können ein const int-Objekt nicht als Fallbezeichnung verwenden (während ein Makro funktioniert)" ---> In Bezug auf diese Anweisung habe ich eine const int-Variable in C getestet, falls sie funktioniert ....
john
8
@john: Nun, Sie müssen den Code, den Sie getestet haben, angeben und den spezifischen Compiler benennen. Die Verwendung von 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.)
AnT
11
"... und sind daher in den meisten praktischen Fällen normalerweise nutzlos ." Ich stimme dir nicht zu. Sie sind sehr nützlich, solange Sie den Namen nicht als konstanten Ausdruck verwenden müssen. Das Wort "Konstante" in C bedeutet etwas, das zur Kompilierungszeit ausgewertet werden kann; constbedeutet schreibgeschützt. const int r = rand();ist vollkommen legal.
Keith Thompson
In c ++ ist es besser zu verwenden constexprals constspeziell mit den stlContainern wie arrayoder bitset.
Mayukh Sarkar
1
@ John Sie müssen in switch()Aussage getestet haben , nicht in caseeiner. Ich bin auch gerade dabei gewesen caught
Hi-Angel
32
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 ... :-)
+1 für die Erwähnung des Speichers, einige eingebettete Systeme haben immer noch nicht so viel, obwohl ich wahrscheinlich anfangen würde, statische Konstanten zu verwenden und nur bei Bedarf zu #defines wechseln würde.
fluffyben
3
Ich habe es gerade getestet. In der Tat verwendet const int im Vergleich zu #define oder enum zusätzlichen Speicher. Da wir eingebettete Systeme programmieren, können wir uns die zusätzliche Speichernutzung nicht leisten. Also werden wir wieder #define oder enum verwenden.
Davide Andrea
2
In der Praxis ist es (nicht mehr) wahr, dass a 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.
Raphael
19
In C #defineist viel beliebter. Sie können diese Werte zum Deklarieren von Arraygrößen verwenden, zum Beispiel:
#define MAXLEN 5void 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
constint maxlen =5;void foo(){int bar[maxlen];}
und sogar weglassen, staticweil die interne Verknüpfung constbereits von [nur in C ++] impliziert wird .
Was meinst du mit "interner Verknüpfung"? Ich kann 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."
Gauthier
@ Gauthier: Entschuldigung. Ich hätte sagen sollen "wird von const bereits in der C ++ - Sprache impliziert". Dies ist spezifisch für C ++.
Sellibitze
@sellibitze es ist schön, einige Argumente auf dem Weg zu sehen, anstatt Tonnen von MEINUNG Wenn es einen Bonus für echte Argumente geben würde, hast du es!
Paul
1
Ab C99 ist Ihr zweites Snippet legal. barist ein VLA (Array variabler Länge); Der Compiler generiert wahrscheinlich Code, als ob seine Länge konstant wäre.
Keith Thompson
14
Ein weiterer Nachteil von constC ist, dass Sie den Wert nicht zum Initialisieren eines anderen verwenden können const.
staticintconst NUMBER_OF_FINGERS_PER_HAND =5;staticintconst NUMBER_OF_HANDS =2;// initializer element is not constant, this does not work.staticintconst 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:
staticuint8_tconst ARRAY_SIZE =16;staticint8_tconst 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 ...
Ein bisschen spät zum Spiel, aber diese Frage tauchte in einer anderen Frage auf. Es 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.
David Hammen
@ David: fundierter Rat, dem ich folgen werde.
Gauthier
1
4 Jahre später haben Sie mir viel Zeit gespart, um herauszufinden, warum ich nicht "nisten" konnte const. Dies könnte mehr positiv bewertet werden!
Plouff
11
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.
Dies ist bei C99 leider nicht der Fall. 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.
AnT
Während es richtig ist, dass C99 dies nicht zulässt, können Sie mit GCC (getestet mit 4.5.3) Arrays mit einer 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).
Raphael
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.
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.
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:
constint 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.
Das Ändern eines konstanten Werts mithilfe eines Zeigers ist ein undefiniertes Verhalten. Wenn Sie bereit sind, dorthin zu gehen, #definekönnen Sie dies auch ändern, indem Sie den Maschinencode bearbeiten.
Ugoren
Du hast teilweise recht. Ich habe den Code mit Visual Studio 2012 getestet und er wird gedruckt 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.
user2229691
3
@ugoren: Angenommen, Sie schreiben #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 ?
Keith Thompson
@ KeithThompson, ich habe nie gesagt, dass es einfach und zuverlässig geht. Nur das #defineist nicht kugelsicher.
Ugoren
3
@ugoren: Mein Punkt ist, dass "Bearbeiten des Maschinencodes" keine sinnvolle Möglichkeit ist, den Effekt der Änderung des Werts von a zu duplizieren #define. Der einzige wirkliche Weg, dies zu tun, besteht darin, den Quellcode zu bearbeiten und neu zu kompilieren.
Keith Thompson
4
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.
"Sie können die Variable nicht sehen" Richtig, es ist keine Variable. Es ändert sich nicht, warum musst du es sehen? Sie können es überall finden, indem Sie einfach nach dem Etikett suchen. Warum sollten (oder wollen) Sie ein #define sehen?
Marshall Eubanks
3
Ü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.
In C sind Aufzählungskonstanten immer vom Typ 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.)
Keith Thompson
@KeithThompson: Seit ich das Obige geschrieben habe, habe ich gelesen, dass MISRA-C kreischt, wenn ein Compiler einen anderen Typ als 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 .
Supercat
Sie können verwenden 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 .
Keith Thompson
1
@KeithThompson: Ich verstehe, dass wir aus historischen Gründen daran festhalten, dass einige Plattformen 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.
Supercat
1
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
constint 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.
Ihre Antwort ist sehr falsch. Hier geht es um C, Ihre Antwort bezieht sich auf C ++, das eine sehr unterschiedliche Semantik für das 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 &.
zu ehrlich für diese Seite
0
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.
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 5
enum { 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.
quelle
enum
ist, dass sie alsint
([C99] 6.7.2.2/3) implementiert sind. Mit A#define
können Sie vorzeichenlose und lange mitU
undL
Suffixe angeben undconst
einen Typ angeben .enum
kann Probleme mit üblichen Typkonvertierungen verursachen.enum
noch#define
zusä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 denstatic const int
, aber der Compiler kann ihn möglicherweise optimieren, wenn Sie keine Adresse angeben.enum
s (undstatic const
): Sie können nicht geändert werden. adefine
kann#undefine
'd sein, wobei anenum
undstatic const
auf den angegebenen Wert festgelegt sind.Allgemein gesagt:
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:
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 :)
quelle
static
übrig bleiben, deren Adresse übernommen wird. und wenn die Adresse genommen wird, hätte man kein#define
oderenum
(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öglicherweiseextern const
stattdessen nach.#if
könnte über vorzuziehen sein ,#ifdef
für boolean Flaggen, aber in diesem Fall wäre es unmöglich machen zu definieren ,var
wie0
von der Kommandozeile. Also in diesem Fall#ifdef
sinnvoller, solange0
ein rechtlicher Wert fürvar
.Speziell in C? In C lautet die richtige Antwort: verwenden
#define
(oder gegebenenfalls)enum
)Während es
const
in 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 int
Objekt nicht alscase
Beschriftung verwenden (solange ein Makro funktioniert). Sie können einconst int
Objekt nicht als Bitfeldbreite verwenden (während ein Makro funktioniert). In C89 / 90 können Sie keinconst
Objekt verwenden, um eine Arraygröße anzugeben (während ein Makro funktioniert). Selbst in C99 können Sie keinconst
Objekt 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
#define
in C zu verwenden . Und vergessen Sie nicht eine andere Alternative, die echte Konstanten in C - erzeugtenum
.In C ++ sind
const
Objekte echte Konstanten, daher ist es in C ++ fast immer besser, dieconst
Variante zu bevorzugen ( in C ++ ist dies jedoch nicht explizit erforderlichstatic
).quelle
const int
Objekten 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.)const
bedeutet schreibgeschützt.const int r = rand();
ist vollkommen legal.constexpr
alsconst
speziell mit denstl
Containern wiearray
oderbitset
.switch()
Aussage getestet haben , nicht incase
einer. Ich bin auch gerade dabei gewesen caughtDer Unterschied zwischen
static const
und#define
besteht 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,#define
wä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 ... :-)
quelle
const
Speicher verwendet. GCC (getestet mit 4.5.3 und einigen neueren Versionen) optimiertconst int
bei 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
#define
ist viel beliebter. Sie können diese Werte zum Deklarieren von Arraygrößen verwenden, zum Beispiel:ANSI C erlaubt Ihnen meines Wissens nicht,
static const
s in diesem Zusammenhang zu verwenden . In C ++ sollten Sie in diesen Fällen Makros vermeiden. Du kannst schreibenund sogar weglassen,
static
weil die interne Verknüpfungconst
bereits von [nur in C ++] impliziert wird .quelle
const int MY_CONSTANT = 5;
in einer Datei haben und in einerextern const int MY_CONSTANT;
anderen darauf zugreifen . Ich konnte im Standard (mindestens C99) keine Informationen zumconst
Ä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."bar
ist ein VLA (Array variabler Länge); Der Compiler generiert wahrscheinlich Code, als ob seine Länge konstant wäre.Ein weiterer Nachteil von
const
C ist, dass Sie den Wert nicht zum Initialisieren eines anderen verwenden könnenconst
.Auch dies funktioniert nicht mit einer Konstante, da der Compiler sie nicht als Konstante betrachtet:
Ich würde gerne
const
in diesen Fällen getippt verwenden, sonst ...quelle
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 256
zehn Ebenen tief in einem Wirrwarr von Headern vergraben sind. Der Name aller GroßbuchstabenARRAY_SIZE
bittet 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 const
hat 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 Sieint foo[var];
als Deklaration schreiben , aber das können Sie nicht (außer als Compiler-Erweiterung "mitstatic const int var = 5;
. Dies ist in C ++ nicht der Fall, wo diestatic const
Version überall verwendet werden#define
kann, wo die Version kann, und ich glaube das ist auch bei C99 der Fall.Benennen Sie jedoch niemals eine
#define
Konstante 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.quelle
const
in C99 ist immer noch keine echte Konstante. Sie können die Arraygröße mit aconst
in 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,const
um die Größe eines Member-Arrays in a zu deklarierenstruct
.const int
Größ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:
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:
quelle
#define var 5
wird Ihnen Probleme bereiten, wenn Sie Dinge wie habenmystruct.var
.Zum Beispiel,
Der Präprozessor ersetzt es und der Code wird nicht kompiliert. Aus diesem Grund schlägt der traditionelle Codierungsstil vor, dass alle Konstanten
#define
Großbuchstaben verwenden, um Konflikte zu vermeiden.quelle
Ich habe ein schnelles Testprogramm geschrieben, um einen Unterschied zu demonstrieren:
Dies kompiliert mit diesen Fehlern und Warnungen:
Beachten Sie, dass enum einen Fehler ausgibt, wenn define eine Warnung ausgibt.
quelle
Die Definition
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:
Dies bedeutet die Definition
Nur so kann ein konstanter Wert definiert werden, der auf keinen Fall geändert werden kann.
quelle
#define
können Sie dies auch ändern, indem Sie den Maschinencode bearbeiten.5
. Aber man kann nicht ändern,#define
weil es ein Präprozessor-Makro ist. Es existiert nicht im Binärprogramm. Wenn man alle Stellen ändern wollte, an denen sieCONST_VALUE
verwendet wurden, musste man dies einzeln tun.#define CONST 5
,if (CONST == 5) { do_this(); } else { do_that(); }
und der Compiler entfernt denelse
Zweig. Wie schlagen Sie vor, den Maschinencode zu bearbeiten, um ihnCONST
auf 6 zu ändern ?#define
ist 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.
quelle
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
#define
ist 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.quelle
Übrigens
#define
ist "enum" eine Alternative zu , die ein angemessenes Scoping bietet, sich aber wie eine "echte" Konstante verhält. Zum Beispiel: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.
quelle
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 unbedingtint
, aber in diesem Fall ist der Typ ist anonym , so dass spielt keine Rolle.)int
eine 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 ihrerchar
Größe in der Lage sein, z. B. einen Typ zu deklarieren, der den Mod 65536 umschließt, selbst wenn der Compiler vieleAND R0,#0xFFFF
oder 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 einemtypedef
foruint16_t
und einer Reihe von#define
s für einzelne Werte weitgehend den gleichen Effekt erzielen .2U < -1L
als wahr und andere als falsch bewertet werden , und wir bleiben jetzt bei der Tatsache, dass einige Plattformen einen Vergleich zwischenuint32_t
undint32_t
als 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:
du kannst nicht tun
printf("address of constant is %p",&mymax);
.Aber mit
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.
quelle
const
Qualifikationsmerkmal hat. C hat keine anderen Symbolikkonstanten als Enum-Konstanten . Aconst int
ist 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 werdenconst
qualifizierte Variablen in den.rodata
Abschnitt 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 int
also für die Typprüfung bevorzugt, während#define
es sich um einen alten Stil handelt. Vielleicht ist es compilerspezifisch. Überprüfen Sie also Ihren erstellten Assembler-Code.quelle
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
#define
muss das Programm, wenn es den Wert d verwendet, nicht zu einem zugewiesenen Speicher springen, sondern nimmt nur den Wert. Wenn#define myValue 7
und das Programm aufrufenmyValue
, verhält es sich genauso wie beim Aufrufen7
.quelle