In der Programmierung ist eine Konstante ein Wert , der während der normalen Programmausführung nicht verändert wird . Wenn sie mit einem Bezeichner verknüpft ist , spricht man von einer „benannten“ Konstante, obwohl die Begriffe „Konstante“ und „benannte Konstante“ oft synonym verwendet werden. Im Gegensatz dazu ist eine Variable ein Bezeichner mit einem Wert, der während der normalen Programmausführung geändert werden kann. Vereinfacht gesagt: Der Wert einer Konstante bleibt konstant, während der Wert einer Variable variiert – daher die unterschiedlichen Bezeichnungen.
Konstanten sind sowohl für Programmierer als auch für Compiler nützlich : Für Programmierer stellen sie eine Form von selbstdokumentierendem Code dar und ermöglichen Rückschlüsse auf die Korrektheit , während sie für Compiler Kompilier- und Laufzeitprüfungen ermöglichen , die überprüfen, ob Konstanzannahmen nicht verletzt werden, [ a ] und einige Compileroptimierungen ermöglichen oder vereinfachen .
Es gibt verschiedene spezifische Realisierungen des allgemeinen Begriffs einer Konstante, mit subtilen Unterschieden, die oft übersehen werden. Die wichtigsten sind: Kompilierzeitkonstanten (statische Werte), Laufzeitkonstanten (dynamische Werte), unveränderliche Objekte und konstante Typen const.
Typische Beispiele für Kompilierzeitkonstanten sind mathematische Konstanten, Werte aus Normen (hier maximale Übertragungseinheit ) oder interne Konfigurationswerte (hier Zeichen pro Zeile ), wie beispielsweise in den folgenden C- Beispielen:
const float PI = 3.1415927 ; // maximale Gleitkomma-Genauigkeit const unsigned int MTU = 1500 ; // Ethernet v2, RFC 894 const unsigned int COLUMNS = 80 ;Typische Beispiele für Laufzeitkonstanten sind Werte, die auf Basis der Eingabewerte einer Funktion berechnet werden, wie beispielsweise in diesem C++- Beispiel:
import std ;using std :: string ;void f ( string & s ) { const size_t len = s . length (); // ... }Verwenden
Some programming languages make an explicit syntactic distinction between constant and variable symbols, for example considering assignment to a constant to be a syntax error, while in other languages they are considered syntactically the same (both simply an identifier), and the difference in treatment is semantic (assignment to an identifier is syntactically valid, but if the identifier is a constant it is semantically invalid).
A constant value is defined once and can be referenced many times throughout a program. Using a constant instead of specifying the same value multiple times can simplify code maintenance (as in don't repeat yourself) and can be self documenting by supplying a meaningful name for a value, for instance, PI instead of 3.1415926.
Comparison with literals and macros
There are several main ways to express a data value that doesn't change during program execution that are consistent across a wide variety of programming languages. One very basic way is by simply writing a literal number, character, or string into the program code, which is straightforward in C, C++, and similar languages.
In assembly language, literal numbers and characters are done using the "immediate mode" instructions available on most microprocessors. The name "immediate" comes from the values being available immediately from the instruction stream, as opposed to loading them indirectly by looking up a memory address.[1] On the other hand, values longer than the microprocessor's word length, such as strings and arrays, are handled indirectly and assemblers generally provide a "data" pseudo-op to embed such data tables in a program.
Another way is by defining a symbolic macro. Many high-level programming languages, and many assemblers, offer a macro facility where the programmer can define, generally at the beginning of a source file or in a separate definition file, names for different values. A preprocessor then replaces these names with the appropriate values before compiling, resulting in something functionally identical to using literals, with the speed advantages of immediate mode. Because it can be difficult to maintain code where all values are written literally, if a value is used in any repetitive or non-obvious way, it is often named by a macro.
Eine dritte Möglichkeit besteht darin, eine Variable als „Konstante“ zu deklarieren und zu definieren. Eine globale oder statische Variable kann (bzw. ein in Assembler definiertes Symbol) mit einem Schlüsselwortqualifizierer wie const`const` constexpr, constant`const` oder final`const` (bei primitiven Datentypen) deklariert werden. Dies bedeutet, dass ihr Wert zur Kompilierzeit festgelegt wird und zur Laufzeit nicht mehr veränderbar ist. Compiler platzieren statische Konstanten üblicherweise im Textbereich einer Objektdatei zusammen mit dem eigentlichen Code, im Gegensatz zum Datenbereich, in dem nicht-konstante initialisierte Daten gespeichert werden. Einige Compiler können einen separaten Bereich für Konstanten erzeugen. Dieser Bereich kann durch Speicherschutz vor dem Überschreiben solcher Konstanten durch fehlerhafte Zeiger geschützt werden.
Diese Konstanten unterscheiden sich in vielerlei Hinsicht von Literalen. Compiler platzieren eine Konstante üblicherweise an einer einzigen Speicherstelle, die durch ein Symbol identifiziert wird, anstatt sie wie ein Makro über die gesamte ausführbare Datei zu verteilen. Dies schließt zwar die Geschwindigkeitsvorteile des Immediate-Mode aus, bietet aber Vorteile hinsichtlich der Speichereffizienz, und Debugger können zur Laufzeit mit diesen Konstanten arbeiten. Während Makros in C und C++ versehentlich durch in Konflikt stehende Headerdateien überschrieben werden können, werden Konflikte mit Konstanten bereits zur Kompilierzeit erkannt.
Je nach Programmiersprache können Konstanten typisiert oder untypisiert sein. In C und C++ sorgen Makros für die typisierte Variante, während const/ constexprdie typisierte Variante bereitstellt.
#define PI 3.1415926535constexpr double PI = 3.1415926535 ; // Kompilierzeitkonstante const double pi = 3.1415926535 ; // LaufzeitkonstanteIn Ada gibt es hingegen universelle numerische Datentypen, die bei Bedarf verwendet werden können:
pi : Konstante := 3.1415926535 ;pi2 : constant float := 3.1415926535 ;wobei die nicht typisierte Variante bei jeder Verwendung implizit in den entsprechenden Typ konvertiert wird. [ 2 ]
Dynamischwertige Konstanten
Neben den oben beschriebenen statischen Konstanten erweitern viele prozedurale Sprachen wie Ada und C++ das Konzept der Konstanz auf globale Variablen, die zur Initialisierungszeit erstellt werden, lokale Variablen, die automatisch zur Laufzeit auf dem Stack oder in Registern erstellt werden, auf dynamisch allokierten Speicher, auf den über Zeiger zugegriffen wird, und auf Parameterlisten in Funktionsköpfen.
Dynamisch definierte Konstanten legen nicht fest, in welchem Speicherbereich sich eine Variable befindet, und ihre Werte werden auch nicht zur Kompilierzeit festgelegt. In C++-Code wie beispielsweise
float func ( const float myFloat ) { const float result = someGlobalVariable * someOtherFunction ( myFloat ); // ... }Der Ausdruck, mit dem die Konstante initialisiert wird, ist selbst nicht konstant. Die Verwendung von Konstanz ist hier für die Programmgültigkeit oder semantische Korrektheit nicht notwendig, bietet aber drei Vorteile:
- Dem Leser ist klar, dass das Objekt nach dem Festlegen nicht mehr verändert wird.
- Versuche, den Wert des Objekts zu ändern (durch spätere Programmierer, die die Programmlogik nicht vollständig verstehen), werden vom Compiler abgelehnt.
- Der Compiler kann möglicherweise Codeoptimierungen durchführen, da er weiß, dass sich der Wert des Objekts nach seiner Erstellung nicht mehr ändert. [ 3 ]
Dynamisch bewertete Konstanten wurden mit ALGOL 68 als Sprachmerkmal eingeführt . [ 3 ] Untersuchungen von Ada- und C++-Code haben gezeigt, dass dynamisch bewertete Konstanten selten verwendet werden, typischerweise für 1 % oder weniger der Objekte, obwohl ihr Einsatz deutlich häufiger möglich wäre, da etwa 40–50 % der lokalen, nicht-klassenspezifischen Objekte nach ihrer Erstellung invariant sind. [ 3 ] [ 4 ] Andererseits sind solche „unveränderlichen Variablen“ in funktionalen Sprachen tendenziell Standard , da diese Programmierstile ohne Seiteneffekte (z. B. Rekursion) bevorzugen oder die meisten Deklarationen standardmäßig unveränderlich machen, wie beispielsweise ML . Rein funktionale Sprachen verbieten Seiteneffekte sogar gänzlich.
Konstanz wird häufig in Funktionsdeklarationen verwendet, um zu gewährleisten, dass ein per Referenz übergebenes Objekt von der aufgerufenen Funktion nicht verändert wird. Je nach Syntax kann entweder ein Zeiger oder das Objekt, auf das gezeigt wird, konstant sein; üblicherweise ist jedoch Letzteres erwünscht. Insbesondere in C++ und C wird die Disziplin, die sicherstellt, dass die Datenstrukturen im gesamten Programm konstant sind, als Konstanzkorrektheit bezeichnet .
Konstante Funktionsparameter
In C/C++ können Parameter von Funktionen oder Methoden als Konstanten deklariert werden. Dadurch wird sichergestellt, dass diese Parameter nach ihrer Initialisierung durch den Aufrufer nicht versehentlich verändert werden können. Handelt es sich um einen vordefinierten (eingebauten) Typ, wird der Parameter per Wert übergeben und kann nicht verändert werden. Bei einem benutzerdefinierten Typ ist die Variable eine Zeigeradresse, die ebenfalls nicht verändert werden kann. Der Inhalt des Objekts hingegen kann uneingeschränkt verändert werden. Die Deklaration von Parametern als Konstanten kann signalisieren, dass dieser Wert nicht geändert werden soll . Programmierer müssen jedoch beachten, dass der Compiler keine Überprüfung auf Objektänderungen durchführen kann.
Neben dieser Funktion ist es in C++ auch möglich, eine Funktion oder Methode als `.` zu deklarieren const. Dies verhindert, dass solche Funktionen oder Methoden etwas anderes als lokale Variablen verändern.
In C# constexistiert das Schlüsselwort zwar, hat aber nicht dieselbe Wirkung auf Funktionsparameter wie in C/C++. Es gibt jedoch eine Möglichkeit, den Compiler dazu zu bringen, diese Prüfung durchzuführen, auch wenn dies etwas knifflig ist. [ 5 ]
Objektorientierte Konstanten
Eine konstante Datenstruktur oder ein Objekt wird in der objektorientierten Programmierung als „ unveränderlich “ bezeichnet . Die Unveränderlichkeit eines Objekts bietet einige Vorteile beim Programmentwurf. Beispielsweise kann es einfach durch Kopieren seines Zeigers oder seiner Referenz „kopiert“ werden, wodurch ein zeitaufwändiger Kopiervorgang vermieden und Speicherplatz gespart wird.
Objektorientierte Sprachen wie C++ erweitern die Konstanz noch weiter. Einzelne Elemente einer Struktur oder Klasse können als `const` deklariert werden, selbst wenn die Klasse selbst nicht konstant ist. Umgekehrt mutableerlaubt das Schlüsselwort `const` die Änderung eines Klassenelements, selbst wenn ein Objekt als `const` instanziiert wurde const. Dies kann in Fällen nützlich sein, in denen nur die Daten konstant sein sollen const, sich aber Metadaten oder klassenweite Informationen ändern können.
Auch Funktionen können in C++ als const deklariert werden. Das bedeutet, dass für ein als const instanziiertes Objekt nur eine const-Funktion aufgerufen werden kann; eine const-Funktion verändert keine unveränderlichen Daten.
C# kennt sowohl den a- constals auch den a- readonlyQualifizierer; const ist nur für Kompilierzeitkonstanten gedacht, während a readonlyin Konstruktoren und anderen Laufzeitanwendungen verwendet werden kann.
In C++ gibt es vier verschiedene Arten von const:
const, die traditionelle Laufzeitkonstanteconstexpr, eine Konstante oder ein Ausdruck, der zur Kompilierzeit ausgewertet werden kann.consteval, die besagt, dass jeder Funktionsaufruf einen Wert erzeugen muss, der ein Kompilierzeitausdruck ist.constinit, was besagt, dass der Ausdruck eine statische (konstante) Initialisierung hat.
In Rust sind alle Deklarationen konstant, sofern sie nicht als `constant` deklariert werden mut, wodurch sie veränderbar werden. Für Kompilierzeitkonstanten constwird `constant` verwendet.
Java
Java has a qualifier called final that prevents changing a reference and makes sure it will never point to a different object. This does not prevent changes to the referred object itself. Java's final is basically equivalent to a constpointer in C++. It does not provide the other features of const.
In Java, the qualifier final states that the affected data member or variable is not assignable, as below:
finalinti=3;i=4;// Error! Cannot modify a "final" objectIt must be decidable by the compilers where the variable with the final marker is initialized, and it must be performed only once, or the class will not compile. Java's final and C++'s const keywords have the same meaning when applied with primitive variables.
constinti=3;// C++ declarationi=4;// Error!Considering pointers, a final reference in Java means something similar to const pointer in C++. In C++, one can declare a "constant pointer type".
Foo*constbar=mem_location;// const pointer typeHere, bar must be initialised at the time of declaration and cannot be changed again, but what it points is modifiable. I.e. *bar=value is valid. It just can't point to another location. Final references in Java work the same way except that they can be declared uninitialized.
finalFooi;// a Java declarationNote: Java does not support pointers.[6] It is because pointers (with restrictions) are the default way of accessing objects in Java, and Java does not use stars to indicate them. For example, i in the last example is a pointer and can be used to access the instance.
One can also declare a pointer to "read-only" data in C++.
constFoo*bar;Here bar can be modified to point anything, anytime; just that pointed value cannot be modified throughbar pointer.
There is no equivalent mechanism in Java. Thus there are also no const methods. Const-correctness cannot be enforced in Java, although by use of interfaces and defining a read-only interface to the class and passing this around, one can ensure that objects can be passed around the system in a way that they cannot be modified.
Das Java Collections Framework bietet eine Möglichkeit, einen unveränderlichen Wrapper über Methoden wie ` Collectionvia` und ähnliche zu erstellen.Collections.unmodifiableCollection()
Eine Methode in Java kann als "final" deklariert werden, was bedeutet, dass sie in Unterklassen nicht überschrieben werden kann.
Java reserviert das constSchlüsselwort, es wird aber derzeit nicht verwendet.
C#
In C# hat der Qualifizierer readonlydieselbe Wirkung auf Datenmember wie finalin Java und der constModifizierer in C++; der Modifizierer consthat eine ähnliche Wirkung wie der constexprin C/C++. Die andere, die Vererbung hemmende Wirkung des Java-Qualifizierers finalbei Anwendung auf Methoden und Klassen wird in C# mithilfe des Schlüsselworts `const` erreicht sealed.
Im Gegensatz zu C++ erlaubt C# nicht, Methoden und Parameter als schreibgeschützt zu kennzeichnen const. Man kann jedoch schreibgeschützte Unterklassen übergeben, und das .NET Framework bietet Unterstützung für die Konvertierung veränderlicher Sammlungen in unveränderliche, die als schreibgeschützte Wrapper übergeben werden können.
Nach Paradigma
Die Behandlung von Konstanten variiert stark je nach Programmierparadigma . Die Konstantenkorrektheit ist in imperativen Sprachen wie C++ problematisch, da Namensbindungen standardmäßig typischerweise Variablen erzeugen , die – wie der Name schon sagt – variieren können. Möchte man eine Bindung als Konstante kennzeichnen, ist daher eine zusätzliche Angabe erforderlich. [ b ] In anderen Programmiersprachenparadigmen treten ähnliche Probleme auf, wobei es auch hier Analogien zur Konstantenkorrektheit gibt.
In der funktionalen Programmierung sind Daten typischerweise standardmäßig konstant und nicht variabel. Anstatt einer Variablen (einem Speicherplatz mit einem Namen und potenziell variablem Wert) einen Wert zuzuweisen, wird eine Bindung zwischen Name und Wert hergestellt, beispielsweise durch die Konstruktion `const` in vielen Lisplet -Dialekten . In manchen funktionalen Sprachen, insbesondere in Multiparadigmen-Sprachen wie Common Lisp , ist die Modifizierung von Daten üblich, während sie in anderen vermieden oder als Ausnahme betrachtet wird. Dies ist beispielsweise bei Scheme (einem weiteren Lisp-Dialekt) der Fall, das die Konstruktion `const` zur Datenmodifikation verwendet , wobei das Ausrufezeichen `!` darauf hinweist. Solche Sprachen erreichen standardmäßig die Konstanzkorrektheit und lenken die Aufmerksamkeit auf die Modifizierung anstatt auf die Konstanz. set!
In vielen objektorientierten Sprachen gibt es das Konzept des unveränderlichen Objekts , das insbesondere für grundlegende Datentypen wie Zeichenketten verwendet wird; bekannte Beispiele sind Java, JavaScript , Python und C#. Diese Sprachen unterscheiden sich darin, ob benutzerdefinierte Datentypen als unveränderlich gekennzeichnet werden können und ob bestimmte Felder (Attribute) eines Objekts oder Datentyps als unveränderlich markiert werden können.
In einigen Multiparadigmensprachen, die sowohl objektorientierte als auch funktionale Stile ermöglichen, können beide Merkmale kombiniert werden. In OCaml beispielsweise sind Objektfelder standardmäßig unveränderlich und müssen explizit mit dem Schlüsselwort `unveränderlich` gekennzeichnet werden mutable, um veränderlich zu sein. In Scala hingegen sind Bindungen explizit unveränderlich, wenn sie mit `unveränderlich` valfür „Werte“ definiert werden, und explizit veränderlich, wenn sie mit var`unveränderlich` für „Variablen“ definiert werden.
Namenskonventionen
Die Namenskonventionen für Konstanten variieren. Manche benennen sie einfach wie jede andere Variable. Andere verwenden Großbuchstaben und Unterstriche für Konstanten, ähnlich wie bei symbolischen Makros SOME_CONSTANT. [ 7 ] In der ungarischen Notation kennzeichnet ein „k“ -Präfix sowohl Konstanten als auch Makros und Aufzählungstypen .
Eine geltende Konvention in Ruby ist , dass jede Variable, die mit einem Großbuchstaben beginnt, als Konstante betrachtet wird, einschließlich Klassennamen.
Siehe auch
- Adresskonstanten für die IBM/360- und Z/Architecture- Plattform
Anmerkungen
- ^ In einigen Fällen kann dies umgangen werden, z. B. durch Verwendung von selbstmodifizierendem Code oder durch Überschreiben der Speicherstelle , an der der Wert gespeichert ist.
- ^ Dies ist nicht universell: In Ada sind beispielsweise Eingabeparameter und Schleifenparameter implizit konstant.
Referenzen
- ^ Beispiel: IBM Systems Information . Befehlssatz - Assembler-Sprachreferenz für PowerPC.
- ^ Booch, Grady (1983). Software Engineering with Ada . Benjamin Cummings . S. 116–117 . ISBN 0-8053-0600-5Die
- ^ a b c Schilling, Jonathan L. (April 1995). „Dynamically-Valued Constants: An Underused Language Feature“ . SIGPLAN Notices . 30 (4): 13–20 . doi : 10.1145/202176.202177 . S2CID 17489672 .
- ^ Perkins, JA Programmierpraktiken: Analyse des für die Luftwaffe, das Heer und die Marine entwickelten Ada-Quellcodes . Proceedings TRI-Ada '89. S. 342–354 . doi : 10.1145/74261.74287 .
- ^ Timwi (09.09.2010). „Schreibgeschützte („const“-ähnliche) Funktionsparameter von C#“ . Stack Overflow . Abgerufen am 06.05.2012 .
[...] Dann können Sie Methoden deklarieren, deren Parametertyp angibt, ob die Variable verändert werden soll oder nicht: [...] Dies ahmt Kompilierzeitprüfungen nach, ähnlich der Konstanz in C++. Wie Eric Lippert richtig bemerkte, ist dies nicht dasselbe wie Unveränderlichkeit. Aber als C++-Programmierer wissen Sie das sicher.
- ^ „Oracle Technology Network für Java-Entwickler | Oracle Technology Network | Oracle“ . Java.sun.com. 14.08.2013 . Abgerufen am 18.08.2013 .
- ^ Microsoft Office XP-Entwickler: Konstante Namen