Forum: Compiler & IDEs Konstante Pointer in C++


von rw (Gast)


Lesenswert?

Hallo,

ich entwickle eine Anwendung in C++ mit msp430-gcc 3.3.6.
Wenn ich Konstanten in einer Klasse definiere, dann mache ich das 
meistens über Enumerationen, weil die dann keinen Speicher belegen. Wenn 
mir der Typ wichtig ist, verwende ich statische konstante 
Membervariablen. Für ganzzahlige Typen funktioniert das auch einwandfrei 
und die Konstante wird vom Compiler wegoptimiert wie es sich gehört:
1
class MemoryManager
2
{
3
  static const uint16 BEGINOF_RAM = 0x4711;
4
  // ...
5
}

Nun möchte ich aber eigentlich, dass BEGINOF_RAM eine Pointerkonstante 
ist, etwa so:
1
class MemoryManager
2
{
3
  static const char* BEGINOF_RAM;
4
  // ...
5
}
6
7
const char* MemoryManager::BEGINOF_RAM = (const char*)0x4711;

Aber für diese Konstante werden zwei Byte im RAM verschwendet. 
Deklariere ich das dagegen so:
1
class MemoryManager
2
{
3
  static const void* const BEGINOF_RAM;
4
  // ...
5
}
6
7
const void* const MemoryManager::BEGINOF_RAM = (const void* const)0x4711;

dann belegt der Pointer zwar immer noch 2 Byte, steht aber in der 
.text-Sektion im ROM. Möchte ich den Pointer aber jetzt im Programm 
verwenden, etwa so:
1
for (char* pos = BEGINOF_RAM; pos < BEGIN_OF_RAM + SIZE; pos++)
2
{
3
  // ...
4
}
so meckert der Compiler, dass er const char* const nicht einer 
char*-Variable zuweisen kann, was ja durchaus auch verständlich ist.

Aber wie kann ich denn nun eine Pointerkonstante anlegen, die ähnlich 
wie Enumerationen wegoptimiert wird und typsicher ist? Bleibt mir nichts 
anderes übrig, als den dreckigen Weg über
1
class MemoryManager
2
{
3
  enum Address
4
  {
5
    BEGINOF_RAM = 0x1234,
6
    SIZE = 1000
7
  }
8
}
9
10
for (char* pos = (char*)BEGINOF_RAM; pos < (char*)BEGIN_OF_RAM + SIZE; pos++)
11
{
12
  // ...
13
}

zu gehen? Möglicherweise ist das aber auch gar nicht so dreckig, denn 
durch den cast sieht man immer sofort, was es eigentlich sein soll. 
Welche Meinung habt ihr?

Vielen Dank.

von (prx) A. K. (prx)


Lesenswert?

const char *p = variabler Pointer auf Konstante.
char *const p = konstanter Pointer auf Variable.

Letzteren kannst du einem "char *" ohne cast zuweisen.

von Karl H. (kbuchegg)


Lesenswert?

rw schrieb:

A. K. hat es ja schon anklingen lassen

>
1
> class MemoryManager
2
> {
3
>   static const char* BEGINOF_RAM;
4
>   // ...
5
> }
6
> 
7
> const char* MemoryManager::BEGINOF_RAM = (const char*)0x4711;
8
> 
9
>
>
> Aber für diese Konstante werden zwei Byte im RAM verschwendet.

Er hat genau das gemacht, was du ihm gesagt hast.
BEGINOF_RAM ist ein Pointer. Punkt
Dieser Pointer zeigt auf Character die konstant sind. Punkt

Der Pointer kan sich ändern, nicht jedoch das worauf er zeigt. Der 
Compiler har gar keine andere Wahl, als für den Pointer ein paar Bytes 
im Ram zu reservieren, denn ... der Pointerwert selbst ist ja variabel.

-> Du hast hier keine Konstante definiert, sondern eine Variable.

1
class MemoryManager
2
{
3
  static char * const BEGINOF_RAM;
4
  // ...
5
}
6
 
7
char * const MemoryManager::BEGINOF_RAM = (char*)0x4711;

NB: Der richtige Datentyp für 'Byte' ist unsigned char und nicht char. 
char benutzt man für Zeichenketten. Bei allen anderen Dingen überlässt 
man niemals dem Compiler die Wahl, ob er einen char als signed oder 
unsigned ansehen will.

von Karl H. (kbuchegg)


Lesenswert?

rw schrieb:

> zu gehen? Möglicherweise ist das aber auch gar nicht so dreckig, denn
> durch den cast sieht man immer sofort, was es eigentlich sein soll.
> Welche Meinung habt ihr?

Dass du deine Sichtweise ändern solltest:
Jeder Cast ist von übel. Wenn du einen Cast benötigst um dir Dinge 
zurechtzucasten, solltest du dich zurücklehnen und darüber nachdenken, 
ob dieser Cast nicht eigentlich ein Indiz dafür ist, dass in deiner 
restlichen Struktur etwas nicht stimmt.

Cast sind Waffen! Man setzt sie niemals leichtfertig ein.

von Sven P. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> NB: Der richtige Datentyp für 'Byte' ist unsigned char und nicht char.
> char benutzt man für Zeichenketten. Bei allen anderen Dingen überlässt
> man niemals dem Compiler die Wahl, ob er einen char als signed oder
> unsigned ansehen will.
Das sowieso.
Ansonsten würde ich als 'richtigen Datentyp für Byte' sogar eher noch 
uint8_t vorschlagen, hm?

von Mark B. (markbrandis)


Lesenswert?

Karl heinz Buchegger schrieb:
> Dass du deine Sichtweise ändern solltest:
> Jeder Cast ist von übel.

Na, na. Wenn ich Polymorphie in C++ haben will und Objekte von einer 
abstrakten Basisklasse ableite (z.B. Rechtecke, Kreise, etc. von der 
Klasse GraphicObject), dann werde ich so ganz ohne casts wohl nicht 
auskommen. Zumal man die Objekte vielleicht gerne noch in einen 
Container steckt und drüber iteriert, aber dann auch mal an einzelne 
Member eines bestimmte Objekts dran will, und das alles mit demselben 
ungecasteten Zeiger wäre doch irgendwie schwierig :-)

von Rolf Magnus (Gast)


Lesenswert?

> Na, na. Wenn ich Polymorphie in C++ haben will und Objekte von einer
> abstrakten Basisklasse ableite (z.B. Rechtecke, Kreise, etc. von der
> Klasse GraphicObject), dann werde ich so ganz ohne casts wohl nicht
> auskommen.

Man wird (oder sollte zumindest) sie aber sehr selten wirklich brauchen.

> Zumal man die Objekte vielleicht gerne noch in einen Container steckt
> und drüber iteriert, aber dann auch mal an einzelne Member eines
> bestimmte Objekts dran will, und das alles mit demselben ungecasteten
> Zeiger wäre doch irgendwie schwierig :-)

Die Frage wäre, warum du an einer Stelle, wo du generisch über den 
ganzen Container iterierst, Funktionen brauchst, die es nur in einer 
ganz bestimmten abgeleiteten Klasse gibt.

von Mark B. (markbrandis)


Lesenswert?

Rolf Magnus schrieb:
> Die Frage wäre, warum du an einer Stelle, wo du generisch über den
> ganzen Container iterierst, Funktionen brauchst, die es nur in einer
> ganz bestimmten abgeleiteten Klasse gibt.

Nicht an der exakt gleichen Stelle, aber... hm, ich bring das Visitor 
Pattern grad nicht mehr ganz im Kopf zusammen, aber da war was.

von rw (Gast)


Lesenswert?

Danke für die Antworten.
Ich hab grad nochmal meinen Stroustrup genauer gelesen und siehe da: es 
steht ja eindeutig da, dass bei
1
const char*
 sich das const auf den Basistyp, also char bezieht und bei
1
char* const
 ein konstanter Pointer gemeint ist. Ich bin der Meinung, das stand 
gestern noch nicht drin ;-)

Und wenn ich den Pointer nur für Adressberechnungen brauche bei denen 
ich byteweise rechnen muss, aber nicht wirklich an seinem Ziel 
interessiert bin, ist es doch völlig okay char* zu verwenden, oder?
Ich habe noch gesehen, dass manche das char* hinter einem caddr_t 
verstecken, aber an sich erhöht das ja höchstens die Leserlichkeit.

Also danke nochmal.

von rw (Gast)


Lesenswert?

Die zwei Adressbytes werden nun im ROM verbraucht. Mit enums würde es 
noch kleiner gehen, aber immerhin habe ich erreicht, was ich wollte. Nun 
frage ich mich nur noch, warum man Pointerkonstanten nicht in 
Headerfiles definieren darf wie Integerkonstanten auch.

von Rolf Magnus (Gast)


Lesenswert?

> Nun frage ich mich nur noch, warum man Pointerkonstanten nicht in
> Headerfiles definieren darf wie Integerkonstanten auch.

Nicht die Pointerkonstanten, sondern die Integerkonstanten bilden hier 
die Ausnahme. Viel mehr als ein "weil das in C++ nun mal so 
spezifiziert" ist, kann man als Antwort allerdings nicht bringen.

von Karl H. (kbuchegg)


Lesenswert?

Mark Brandis schrieb:
> Karl heinz Buchegger schrieb:
>> Dass du deine Sichtweise ändern solltest:
>> Jeder Cast ist von übel.
>
> Na, na. Wenn ich Polymorphie in C++ haben will und Objekte von einer
> abstrakten Basisklasse ableite (z.B. Rechtecke, Kreise, etc. von der
> Klasse GraphicObject), dann werde ich so ganz ohne casts wohl nicht
> auskommen.

Wozu willst du da casten (im Regelfall. Ausnahmen gibt es immer)

> Zumal man die Objekte vielleicht gerne noch in einen
> Container steckt und drüber iteriert, aber dann auch mal an einzelne
> Member eines bestimmte Objekts dran will, und das alles mit demselben
> ungecasteten Zeiger wäre doch irgendwie schwierig :-)

Die richtige Art wäre es, das Objekt über eine virtuelle Funktion nach 
diesem Member zu befragen und nicht den Pointer den du hast 
zurechtzucasten.

Genau das ist dann nämlich Polymorphie, wie sie in C++ verstanden wird: 
Die Polymorphie wird über virtuelle Function Calls aufgelöst.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

> Nicht die Pointerkonstanten, sondern die Integerkonstanten bilden hier
> die Ausnahme. Viel mehr als ein "weil das in C++ nun mal so
> spezifiziert" ist, kann man als Antwort allerdings nicht bringen.

Die vom Schema abweichenden Integer-Konstanten in C++ haben wir 
Stroustrups gesunder Abneigung gegenüber dem Präprozessor zu verdanken.
Das Sammelsurium diverser #define MAX_SONSTWAS 10 nervte ihn.

Andererseits konnte man nicht alles was "const" heisst so behandeln, 
insofern war die Inkonsequenz unvermeidlich.

von Rolf Magnus (Gast)


Lesenswert?

> Andererseits konnte man nicht alles was "const" heisst so behandeln,
> insofern war die Inkonsequenz unvermeidlich.

Warum sollte man einen Zeiger oder einen double nicht genauso behandeln 
können? Bei komplexeren Typen mag's ja anders sein, aber was macht denn 
einen Integer so speziell, daß das nur da gehen soll?

von (prx) A. K. (prx)


Lesenswert?

Kann man auch. Hab mal im Standard und diversen Compilern nachgesehen.

Der Standard, jedenfalls die mir vorliegende nicht unbedingt endgültige 
Version, legt eindeutig und ohne Einschränkung fest, das "const" 
Variablen internal linkage haben.

3 verschiedene Compiler (DM,MS,GCC) teilen diese Ansicht. Es ist sehr 
wohl möglich, auch Daten vom Typ double oder auch pointer in separaten 
compilation units mit gleichem Namen und gleichem oder unterschiedlichem 
Wert zu verwenden.

Insofern ist also überhaupt keine Inkonsequenz vorhanden. const 
Variablen ausserhalb von Klassen sind sofern nicht anders angegeben 
implizit static. Will man das C Verhalten reproduzieren, dann muss man 
auf extern const zurückgreifen (extern const int n = 10). Egal ob es 
sich um Basisdatentypen oder abgeleitete Typen handelt.

Einfacher ausgedrückt: Man kann in einem C++ (!) include file durchaus 
auch const pointer unterbringen.

Man muss sich dabei nur darüber im Klaren sein, dass ggf. in jeder 
compilation unit Platz dafür angelegt wird, wenn dem Compiler danach 
zumute ist.

von Rolf Magnus (Gast)


Lesenswert?

> Der Standard, jedenfalls die mir vorliegende nicht unbedingt endgültige
> Version, legt eindeutig und ohne Einschränkung fest, das "const"
> Variablen internal linkage haben.

Ist mir bekannt, aber darum ging es nicht.

> Insofern ist also überhaupt keine Inkonsequenz vorhanden. const
> Variablen ausserhalb von Klassen sind sofern nicht anders angegeben
> implizit static.

Aber folgendes geht eben trotzdem nicht:
1
class Foo
2
{
3
public:
4
    double pi = 3.1415;
5
};

> Einfacher ausgedrückt: Man kann in einem C++ (!) include file durchaus
> auch const pointer unterbringen.

Aber nicht als static-Member einer Klasse. Da gehen ausschließlich 
Integer-Typen (oder genauer gesagt "integral types" und enums).

von (prx) A. K. (prx)


Lesenswert?

Dieses Beispiel geht mit int natürlich ebensowenig. Und als static const 
frisst GCC sowohl int wie auch double. Erst bei abgeleiteten Typen ist 
Schluss.

von Rolf Magnus (Gast)


Lesenswert?

> Dieses Beispiel geht mit "int" ebensowenig.

Ups, Flüchtigkeitsfehler. Natürlich hätte es eigentlich so heißen 
sollen:
1
class Foo
2
{
3
public:
4
    static const double pi = 3.1415;
5
};

> Und als "static const" frisst GCC sowohl int wie auch double.

Das ist eine Compiler-Erweiterung, die defaultmäßig an ist. Versuch's 
mal mit -ansi -pedantic.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.