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
classMemoryManager
2
{
3
staticconstuint16BEGINOF_RAM=0x4711;
4
// ...
5
}
Nun möchte ich aber eigentlich, dass BEGINOF_RAM eine Pointerkonstante
ist, etwa so:
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:
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
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.
const char *p = variabler Pointer auf Konstante.
char *const p = konstanter Pointer auf Variable.
Letzteren kannst du einem "char *" ohne cast zuweisen.
>> 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.
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.
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.
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?
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 :-)
> 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.
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.
Danke für die Antworten.
Ich hab grad nochmal meinen Stroustrup genauer gelesen und siehe da: es
steht ja eindeutig da, dass bei
1
constchar*
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.
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.
> 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.
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.
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.
> 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?
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.
> 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
classFoo
2
{
3
public:
4
doublepi=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).
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.
> Dieses Beispiel geht mit "int" ebensowenig.
Ups, Flüchtigkeitsfehler. Natürlich hätte es eigentlich so heißen
sollen:
1
classFoo
2
{
3
public:
4
staticconstdoublepi=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.