Einführung in zu einer Verbundanweisung lokale Konstanten in C++ im Rahmen der Lehre des Programmierens mit der Programmiersprache C++. [] (Block, Konstante, Konstantendefinition, lokale Konstante, Verbundanweisung), Lektion, Seite 721446
http://www.purl.org/stefan_ram/pub/c++_blockkonstanten_de (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Blockkonstanten in C++

Eine Konstante kann auch in einem Block definiert werden. Solch eine Konstante wird auch als eine „(zu einem Block) lokale Konstante“ bezeichnet.

constant.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ ::std::cout << 12 << '\n'; 
int const zwoelf( 12 ); ::std::cout << zwoelf << '\n'; }

std::cout
12 
12

Der Name der Konstante kann verändert werden, ohne daß dies die Funktion des Programmes verändert. Ein irreführender Name, wie in dem folgenden Beispiel, sollte natürlich vermieden werden. Doch für den Computer ist der Name ohne Bedeutung: Er verknüpft nur die verschiedenen Stellen seiner Verwendung.

Bei der Ersetzung eines Namens durch einen anderen, muß dieser überall ersetzt werden, wenn sich die Bedeutung nicht ändern soll.

constant1.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ ::std::cout << 12 << '\n'; 
int const zehn( 12 ); ::std::cout << zehn << '\n'; }

std::cout
12 
12

Typwandlung bei Initialisierung

Die Festlegung des Wertes der Konstante beim Anlegen wird auch als Initialisierung  bezeichnet. Bei der Initialisierung einer Konstanten kann ein Ausdruck verwendet werden, der nicht denselben Typ hat wie die Konstante, wenn C++  für diesen Fall eine Umwandlung vorsieht. Dies ist ähnlich wie beim Aufruf einer Funktion, die ein Argument vom Typ der Konstanten erwartet mit einem Argument vom Typ des zur Initialisierung verwendeten Ausdrucks. Das folgende Programmbeispiel zeigt, wie von solchen Umwandlungen Gebrauch gemacht wird.

conversion.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ double const d( 300 ); 
int const i( 273.15 ); 
int const j( true ); 
bool const b( 1 ); 
::std::cout << d << ", " << i << ", " << j << ", " << b << '\n'; }

std::cout
300, 273, 1, 1

Anwendungsbeispiel

Das folgende Programm errechnet aus einem benannten Wert »300«, der eine Temperatur in Kelvin darstellen soll, einen anderen benannten Wert: die entsprechende Celsius-Temperatur.

celsius.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ double const t_kelvin( 300 ); 
double const versatz( 273.15 ); 
double const t_celsius( t_kelvin - versatz ); 
::std::cout << t_celsius << '\n'; }

::std::cout
26.85

Ausdrücke, die neben Operatoren nur Konstanten und Literale enthalten, können oft bereits während der Übersetzung eines Programmes ausgewertet werden und kosten daher nur wenig Laufzeit.

Bereiche in C++

Die Zuordnung von Bezeichnern zu ihren Werten ist auf den Textbereich  (Gültigkeitsbereich, scope ) der Bezeichner eingeschränkt. Dieser ist zunächst durch den Block des Bezeichners gegeben. Dieser Block spielt die Rolle eines Namensraumes.

Eindeutigkeit von Bezeichnern

Die Namen eines Bereiches bilden eine Menge: Das bedeutet, daß jeder Namen nur einmal darin vorkommen kann. Daher ist der folgende Text keine korrekte C++ -Übersetzungseinheit.

block.txt
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ int const c( 1 );  
int const c( 2 );  
::std::cout << c << '\n'; }

Diagnose
"block.txt", line 5: error: "c" has already been declared in the current scope 
int const c( 2 );  
^
"block.txt", line 4: warning: variable "c" was declared but never referenced 
{ int const c( 1 );  
^

Ende der Bereiche

In dem folgenden Beispielprogramm gilt die Definition des Bezeichners »c« als Wert »1« nur in dem Block, der mit der zweiten öffnenden geschweiften Klammer beginnt und mit der ersten schließenden geschweiften Klammer endet. In dem folgenden Block kann der gleiche Bezeichner erneut definiert werden.

block0.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ { int const c( 1 ); ::std::cout << c; } 
{ int const c( 2 ); ::std::cout << c; } 
::std::cout << '\n'; }

std::cout
12

block0a.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ { int const c( 1 ); } ::std::cout << c; ::std::cout << '\n'; }

Diagnose
"block0a.cpp", line 4: warning: constant "c" was declared but never referenced 
{ { int const c( 1 ); } ::std::cout << c; ::std::cout << '\n'; } 
^
"block0a.cpp", line 4: error: identifier "c" is undefined 
{ { int const c( 1 ); } ::std::cout << c; ::std::cout << '\n'; } 
^
1 error detected in the compilation of "block0a.cpp".

Verschachtelte Bereiche

Blöcke können ineinander verschachtelt werden, das heißt: ein Block kann einen anderen Block enthalten.

Der innere Block ist dann wieder ein eigener Namensraum. Er „erbt“ alle Definitionen umgebender Namensräume, kann aber alle geerbten Bezeichner in eigenen Definitionen „überschreiben“ (umdefinieren). (Diese Vererbung von Definitionen wird auch bei Klassen verwendet.) Wenn ein innerer Block einen Bezeichner definiert, dann gilt in dem inneren Block auch die Definition des darin definierten Bezeichners ab der Stelle ihres Auftretens. Endet der innere Block, so enden auch die im inneren Block definierten Zuordnungen der Werte zu den Namen.

block1.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ int const c( 9 ); 
int const d( 9 ); 
::std::cout << 0 << c << d; 
{ ::std::cout << 1 << c << d; int const c( 8 ); 
::std::cout << 2 << c << d; } 
::std::cout << 3 << c << d << '\n'; }

std::cout
099199289399

Der Leser kann jetzt vorhersagen, was das folgende Programm ausgibt.

block2.cpp
#include <iostream> // ::std::cout 
#include <ostream> // <<
int main() 
{ int const c( 3 ); 
{ int const c( 4 );  
{ int const c( 5 );  
::std::cout << c; } 
::std::cout << c; } 
::std::cout << c << '\n'; }

Beispiele

Möbelkauf

Wenn Büromöbel von einem Endverbraucher erworben werden, sind im Jahre 2003 noch 16 % Mehrwertsteuer zu zahlen. Es werden zwei Tische für je 420 Euro und eine Lampe für 145 Euro Warenwert erworben und 3 % Rabatt vereinbart. Wieviel Euro sind zu zahlen?

In dem folgenden Programm werden Konstanten verwendet, um die Ausdrücke möglichst verständlich und lesbar zu machen.

Rechnung.cpp
#include <iostream> // ::std::cout 
#include <ostream> // << 
#include <iomanip> // ::std::setprecision 
#include <ios> // ::std::fixed
int main() 
{ // ewige Konstanten 
double const prozent( 0.01 ); 
double const euro ( 100. ); // intern wird in Cent gerechnet
{ // aktuelle Konstanten 
double const mwst( 16. * prozent );
{ // Warenwerte (Preisliste) 
double const tisch( 420 * euro ); 
double const lampe( 145 * euro );
{ // Dieser Einkauf 
double const rabatt( 3. * prozent ); 
double const warenwert( 2 * tisch + 1 * lampe ); 
double const netto( warenwert * ( 100 * prozent - rabatt )); 
double const brutto( netto * ( 100 * prozent + mwst )); 
double const gerundet( static_cast<int>( brutto + 0.5 )); 
double const forderung( gerundet ); 
::std::cout << ::std::fixed << ::std::setprecision( 2 ) << 
"Zu zahlen sind " << forderung / euro << " Euro.\n"; }}}}

std::cout
Zu zahlen sind 1108.32 Euro.

Bitentropie

Das folgende Programm berechnet wieviel Entropie ein Bit hat, dabei erfolgt die Ausgabe in SI-Einheiten.

Die Konstante »J« und die Konstante »K« werden hier praktisch nur als Kommentar eingesetzt, um die Einheiten einer physikalischen Größe angeben zu können.

Man bemerkt an dem folgenden Programm, wie angenehm es ist, daß in C++  zwischen Groß- und Kleinschreibung unterschieden wird, so wie dies ja auch in der deutschen Sprache und bei den physikalischen Namen ist. Daher kann der Bezeichner »K« für die Einheit Kelvin und der Bezeichner »k« für die Boltzmann-Konstante verwendet werden.

Bit.cpp
#include <iostream> // ::std::cout 
#include <ostream> // << 
#include <iomanip> // ::std::setprecision 
#include <ios> // ::std::scientific 
#include <cmath> // ::std::log
int main() 
{ // SI-Einheiten 
double const J( 1. ); // Joule 
double const K( 1. ); // Kelvin
{ // physikalische Konstanten (nach CODATA, Stand 1998) 
double const k( 1.3806503E-23 * J / K ); // Boltzmann-Konstante
{ // Berechnung der Entropie eines Bits 
double const bit( k * ::std::log( 2. ));
{ // Ausgabe 
::std::cout << ::std::scientific << ::std::setprecision( 2 ) << 
"bit = " << bit /( J / K )<< " J/K.\n"; }}}}

std::cout
bit = 9,57e-024 J/K.

Übungsaufgaben

Übungsfrage
pluseins.cpp
#include <iostream> // ::std::cout
#include <ostream> // << int main()
{ double const a( 1 );
::std::cout << a + 1 << '\n';
// Welchen Wert hat die Konstante "a" hier?
}
Welchen Wert hat die Konstante »a« nach der Ausgabe?
Übungsaufgabe
Schreiben Sie in das folgende Programm an die Stelle von »/* ... */« einen Ausdruck, so daß der Wert der Konstanten »inch« die Länge einer Strecke in Zoll angibt, deren Zentimeterlänge durch den Zahlenwert in der Konstanten »cm« angegeben ist. Das Programm sollte also dann einen Wert von ungefähr 0,51 ausgeben. (Wenn man dann jedoch den Wert der Konstanten »cm« verdoppeln würde, dann sollte sich der ausgegebene Wert auch verdoppeln.)
Die ersten vier Zeilen und die letzte Zeile dürfen nicht verändert werden!
umwandlung.cpp
#include <iostream> // ::std::cout
#include <ostream> // << int main()
{ double const l_cm( 1.29 ); /* Laenge in cm (Zentimeter) */
double const l_inch( /* ... */ );
::std::cout << l_inch << '\n'; /* Laenge in Zoll (ca 0,51 Zoll) */ }
Übungsaufgabe
sinus1.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <cmath> // ::std::sin, ::std::sqrt, ::std::pow int main()
{ double const x( 1 );
double const y( ::std::sin( x ));
double const z( ::std::sqrt( y ));
double const u( ::std::pow( y, 0.51 ));
::std::cout << z << '\n';
::std::cout << u << '\n'; }
std::cout
0.917317
0.915735
Schreiben Sie die Einheit »sinus1.cpp« so um, daß dieselben Werte ausgegeben werden und dieselben Numerale und Funktionen zur Berechnung verwendet werden, aber keine Konstanten mehr in dem Programm vorkommen. Ersetzen Sie zunächst die Konstante »x« überall durch ihre Definition, also durch den Wert »1« und entfernen Sie die Definition der Konstante »x«. Nach diesem Schema können dann alle anderen Definitionen eliminiert werden, bis nur noch die beiden Ausgabeausdrücke übrigbleiben.
Übungsaufgabe
sinus2.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <cmath> // ::std::sin, ::std::cos, ::std::log int main()
{ ::std::cout << ::std::sin( 1 + ::std::log( 2.178 ))<< '\n';
::std::cout << ::std::cos( 1 + ::std::log( 2.178 ))<< '\n'; }
std::cout
0.978526
-0.206122
Schreiben Sie die Einheit »sinus2.cpp« so um, daß derzeit wiederholt vorkommende gleiche Teilausdrücke nur einmal vorkommen. Definieren Sie diese dazu als Konstante oder Konstanten und verwenden Sie die Konstante oder Konstanten dann an deren Stellen. Verwenden Sie aber die in der Einheit »sinus.cpp« vorkommenden Literale und Operatoren, ohne die Werte von Teilausdrücken dabei auszurechnen. Die Ausgabe des Programmes darf sich dadurch nicht ändern.

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram721446 stefan_ram:721446 slrprd, slrprdqxx, slrprddoc, slrprd721446, slrprddef721446, PbclevtugFgrsnaEnz

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten.
http://www.purl.org/stefan_ram/pub/c++_blockkonstanten_de