Leseadressen in C++ (Leseadressen in C++), article, Seite 724109
https://www.purl.org/stefan_ram/pub/leseadressen_c++ (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
C++-Kurs

Leseadressen in C++ 

In einer Deklaration wie »int * p« wird der Teil vor dem Stern »*« als Deklarationsspezifizierersequenz (»decl-specifier-seq«) bezeichnet.

In einer Deklarationsspezifizierersequenz können neben Typspezifizierern (wie »auto«, »int«, »double« oder »char«) auch Cv-Qualifizierer (wie »const«) in beliebiger Reihenfolge direkt hintereinander aufgelistet werden. Die Reihenfolge hat keine Bedeutung. Die Deklarationsspezifizierersequenz »auto const« bedeutet dasselbe wie die Deklarationsspezifizierersequenz »const auto«. Zur Vereinfachung beschränken wir uns ab hier auf eine Schreibweise, nämlich die mit dem nachgestellten »const«, also »auto const«.

Wenn T ein Typspezifizierer ist (wie »auto«, »int«, »double« oder »char«), dann nennen wir den Typ »T const *« einen Lesetyp.

Eine Adresse, deren Typ ein Lesetyp ist, nennen wir eine Leseadresse.

Ein Objekt, dessen Typ ein Lesetyp ist, nennen wir eine Leseobjekt. Ein Leseobjekt enthält eine Leseadresse.

Eine Leseadresse kann nur verwendet werden, um aus dem Referenten zu lesen  (sie kann nicht verwendet werden, um etwas in den Referenten zu schreiben ).

Das folgende Programmbeispiel zeigt einen Lesezeiger »r« und eine Adresse »w«. Der Lesezeiger erlaubt es, das Objekt »i« durch Auswertung des Ausdrucks »*r« zu lesen, aber nur die Adresse »w« erlaubt es, mit »*w =« in das Objekt »i« zu schreiben. Würde versucht werden, mit »*r =« zu schreiben, so gäbe es eine Fehlermeldung.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 27 };

auto const * r{ &i };
auto * w{ &i };

::std::cout << *r << '\n';

*w = 40;

::std::cout << *r << '\n'; }

Protokoll
27
40

Der Lesezeiger kann  geändert werden. Im folgenden Beispiel enthält »p« zuerst die Adresse von »i« und später die Adresse von »k«. Ein Lesezeiger ist also selber nicht  notwendigerweise ein konstanter Zeiger.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 27 };
auto k{ 40 };
auto const * p{ &i };
::std::cout << *p << '\n';

p = &k;

::std::cout << *p << '\n'; }

Protokoll
27
40

Die Existenz einer Leseadresse auf ein Objekt bedeutet nicht  unbedingt, daß dieses Objekt konstant ist. Es kann durchaus möglich sein, daß das Objekt direkt oder über eine andere Adresse geändert werden kann.

Das folgende Programm zeigt, wie das Objekt »i« sowohl über »i =« als auch über »*q =« geändert werden kann, es kann nur nicht über »*p =« geändert werden.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 27 };
auto const * p{ &i };
auto * q{ &i }; ::std::cout << *p << '\n';
i = 40; ::std::cout << *p << '\n';
*q = 60; ::std::cout << *p << '\n'; }

Protokoll
27
40
60

Wurde ein Objekt als konstant  gekennzeichnet, so sind nur Leseaddressen dieses Objekts erlaubt!

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto const i{ 27 };
auto const * p{ &i };
::std::cout << *p << '\n';
/* auto * q { &i };
error: initialization discards 'const' qualifier from pointer target type
*/ }

Protokoll
27

Das voranstehende Programmbeispiel zeigt auch noch einmal, daß eine „Konstante“ (also hier »i«) in C  kein richtiger Name für einen Wert ist (das wäre hier der Wert 27), sondern doch erst einmal ein Name für ein Objekt. Dieses Objekt enthält dann der Wert der Konstanten. Wenn dies nicht so wäre, dann könnte man nämlich nicht die Adresse einer Konstanten (also hier »&i«) angeben.

Wenn durch »const« einmal eine Einschränkung festgelegt wurde, dann verhindert das Typsystem von C++  es, daß diese Einschränkung unter Verwendung des Namen, der mit der Einschränkung versehen ist, wieder aufgehoben wird. Daher kann eine Leseadresse im allgemeinen nicht  zur Initialisierung eines Zeigers, der kein Lesezeiger ist, verwendet werden.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto const i { 27 };
auto const * p { &i };
::std::cout << *p << '\n';
/* auto * w { p };
error: initialization discards 'const' qualifier from pointer target type
*/ }

Protokoll
27

Zitate *

»simple-declaration« (C++ 2017e, 10p1):
simple-declaration:
decl-specifier-seq init-declarator-list/opt ;
»decl-specifier-seq« (C++ 2017e, 10p1) (gekürzt):
decl-specifier-seq:
decl-specifier decl-specifier-seq
»decl-specifier« (C++ 2017e, 10.1p1) (gekürzt):
decl-specifier:
defining-type-specifier
»defining-type-specifier« (C++ 2017e, 10.1.7p1) (gekürzt):
defining-type-specifier:
type-specifier
»type-specifier« (C++ 2017e, 10.1.7p1) (gekürzt):
type-specifier:
simple-type-specifier
cv-qualifier
»simple-type-specifier« (C++ 2017e, 10.1.7p1) (gekürzt):
simple-type-specifier:
auto
char
int
double
»cv-qualifier« (C++ 2017e, 10.1.7p1) (gekürzt):
cv-qualifier:
const

Ergänzungen zu Zeigern

In den Unterabschnitten dieses Abschnitts finden sich vorläufig einige Ergänzungen zu Zeigern, für die jeweils weitere Lektionen geplant sind.

const-cast

Man kann eine const-Qualifikation durch einen cast entfernen, was man aber nie tun sollte. Das folgende Programm hat undefiniertes Verhalten, weil damit in ein const-Objekt geschrieben wird.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto const i{ 27 };
auto const * p{ &i };
::std::cout << *p << '\n';
/* auto * w { p };
error: initialization discards 'const' qualifier from pointer target type
*/
auto * w { const_cast< int * >( p ) };
*w = 99;
::std::cout << *p << '\n'; }

Protokoll

27

99

C++ Core Guidelines  (2016-04-07)
ES.50: Don't cast away const
Type safety profile: … Type.3: Don't use const_cast to cast away const (i.e., at all).

»* const« — Konstante Zeiger

Ein konstanter Zeiger kann, wie jede andere konstante Variable, nicht verändert werden. Er muß aber deswegen nicht unbedingt auch ein Lesezeiger  sein. Schreibzugriffe über einen konstanten Zeiger sind also nicht notwendigerweise verboten.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 27 };
auto j{ 33 };
auto * const p { &i };
::std::cout << *p << '\n';
*p = 45;
::std::cout << *p << '\n';
/* p = &j;
error: assignment of read-only variable 'p'
*/ }

Protokoll

27

45

Tabelle

Deklaration erlaubt verboten

int * p p = ..., *p = ...

int const * p p = *p =

int * const p *p = p =

int const * const p p = ..., *p = ...

»( int * p )« — Adreßparameter

Das folgende Programm zeigt eine Funktion, welche den Wert eines int-Objekts, dessen Adresse ihr übergeben wird, auf 0 setzt.

main.cpp

#include <iostream>
#include <ostream>

void clear( int * const p ) { *p = 0; }

int main()
{ auto i{ 12 };
::std::cout << i << '\n';
clear( &i );
::std::cout << i << '\n'; }

Protokoll

12

0

/   Zuweisung

Fügen Sie die Definition einer Funktion »assign« an Stelle der Ellipse »« in das folgende Programm ein, welche den Wert ihres zweiten Arguments in das durch das erste Argument angegebene int-Objekt schreibt.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 7 };
::std::cout << i << '\n';
assign( &i, 34 );
::std::cout << i << '\n'; }

Protokoll
7
34

/   Vertauschen

Fügen Sie die Definition einer Funktion »swap« (Aussprache: /swɑp/) an Stelle der Ellipse »« in das folgende Programm ein, welche die Werte der beiden als Argumente angegebenen int-Objekte miteinander vertauscht.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ auto i{ 17 }; auto j{ 82 };
::std::cout << "i = " << i << ", j = " << j << '\n';
swap( &i, &j );
::std::cout << "i = " << i << ", j = " << j << '\n'; }

Protokoll
i = 16, j = 82
i = 82, j = 16

»return p;« — Adreßrückgaben

Es ist zwar grundsätzlich möglich, eine Adresse zurückzugeben, aber das folgende Programm hat undefiniertes Verhalten, da die zurückgegebene Adresse zu einem Objekt gehört, das nur während der Existenz der Inkarnation von »f« (nur während der Dauer der Auswertung von »f()«) existiert.

main.cpp

#include <iostream>
#include <ostream>

auto f( ){ auto i{ 9 }; return &i; }

int main()
{ ::std::cout << *f() << '\n'; }

Protokoll
(Programm stürzt ab)

»nullptr« — Die Nulladresse

Die Nulladresse ist ein Fluchtwert von Adreßtypen, das heißt, daß ihr Typ  ein Adreßtyp ist, ihr Bedeutung  aber nicht die Bedeutung einer Adresse ist. Sie kann durch Wandlung von »0« oder »nullptr« in einen Adreßtyp erhalten werden und ist falschartig.

Die Nulladresse darf nicht dereferenziert werden. Beim Schreiben von Programmen sollte »nullptr« statt »0« verwendet werden.

main.cpp

#include <iostream>
#include <ostream>

int main()
{ int * n{ 0 };
int * m{ nullptr };
auto i{ 5 };
auto p{ &i };
::std::cout <<( n ? "n" : "-" )<< '\n';
::std::cout <<( m ? "m" : "-" )<< '\n';
::std::cout <<( n == m ? "=" : "-" )<< '\n';
::std::cout <<( n == p ? "=" : "-" )<< '\n';
::std::cout <<( m == p ? "=" : "-" )<< '\n'; }

Protokoll

-

-

=

-

-

»::std::size_t« — Der Größentyp

Der Typ »::std::size_t« ist ein implementationsdefinierter vorzeichenloser ganzzahliger Typ, dessen Werte groß genug werden können, um die Größe jedes Objektes (gemessen in Byte) als Zahl zu umfassen.

main.cpp

#include <cstddef>
#include <iostream>
#include <ostream>

int main()
{ auto const i{ 8 };
::std::size_t const n = sizeof i;
::std::cout << n << '\n'; }

Protokoll
4

 

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. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram724109 stefan_ram:724109 Leseadressen in C++ Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd724109, slrprddef724109, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/leseadressen_c++