Leseadressen in C
»int const *« — Leseaddresse
In einer Spezifizierer-Qualifizierer-Auflistung können Typspezifizierer (wie »int«) und Typqualifizierer (wie »const«) in beliebiger Reihenfolge direkt hintereinander aufgelistet werden. Die Reihenfolge hat keine Bedeutung. Die in der Deklaration verwendete Spezifizierer-Qualifizierer-Auflistung »int const« bedeutet dasselbe wie die Spezifizierer-Qualifizierer-Auflistung »const int«. Zur Vereinfachung beschränken wir uns ab hier auf eine Schreibweise, nämlich die mit dem nachgestellten »const«, also »int const«.
type-specifier:
char
int
double
type-qualifier:
const
specifier-qualifier-list:
type-specifier specifier-qualifier-list/opt
type-qualifier specifier-qualifier-list/opt
Wenn T ein Typ ist (»int«, »char« oder »double«), 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.c
#include <stdio.h>
int main( void )
{ int i = 27;
int const * r = &i;
int * w = &i;
printf( "%d\n", *r );
*w = 40;
printf( "%d\n", *r ); }stdout
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.c
#include <stdio.h>
int main( void )
{ int i = 27;
int k = 40;
int const * p = &i;
printf( "%d\n", *p );
p = &k;
printf( "%d\n", *p ); }stdout
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.c
#include <stdio.h>
int main( void )
{ int i = 27;
int const * p = &i;
int * q = &i;
printf( "%d\n", *p );
i = 40;
printf( "%d\n", *p );
*q = 60;
printf( "%d\n", *p ); }stdout
27
40
60
Wurde ein Objekt als konstant gekennzeichnet, so sind nur Leseaddressen dieses Objekts erlaubt!
main.c
#include <stdio.h>
int main( void )
{ int const i = 27;
int const * p = &i;
printf( "%d\n", *p );
/* int * q = &i;
error: initialization discards 'const' qualifier from pointer target type */ }stdout
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.
(Die oben gezeigte Fehlermeldung könnte unter einigen Implementation auch als bloße Warnung erscheinen.)
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.c
#include <stdio.h>
int main( void )
{ int const i = 27;
int const * p = &i;
printf( "%d\n", *p );
/* int * w = p;
error: initialization discards 'const' qualifier from pointer target type */ }stdout
27