Forum: Mikrocontroller und Digitale Elektronik Cast von [Menge an Bytes] in [Klasse] erzwingen


von Alexander I. (daedalus)


Lesenswert?

Hallo,

angenommen ich hätte folgende Klasse:
1
class ABC
2
{
3
public:
4
 unsigned char testvar1;
5
 unsigned char testvar2;
6
 unsigned int testvar3;
7
8
 ABC(void *data);
9
};
10
11
ABC::ABC(void *data)
12
{
13
 testvar1 = *((unsigned char*)data);
14
 testvar2 = *((unsigned char*)data+1*sizeof(char));
15
 testvar3 = *((unsigned int*)data+2*sizeof(char));
16
}

Der Konstruktor bietet die Möglichkeit, einen Pointer zu übergeben. 
Somit kann ich quasi eine beliebige Speicheradresse in die Klasse ABC 
konvertieren, allerdings erstelle ich ja nur ein neues Objekt, das 
dieselben Werte besitzt wie die Speicheradresse+Offsets. Es sei mal 
dahin gestellt ob an der entsprechenden Position sinnvolle Daten stehen.

Im Speicher steht oben abgebildete Klasse ungefähr so im Speicher:
[testvar][testvar2][testvar3]

Gibt es jetzt eine Funktion wie ich sagen kann: Ab genau dieser 
Speicheradresse bitte in ein Objekt der Klasse ABC umwandeln und ich mir 
somit den "Umweg" über den Konstruktor und die Umkopiererei sparen kann?

Beispiel:
Adresse:  ... #1 #2 #3 #4 #5 #6 #7 #8 #9 #A ...
Speicher: ... 01 02 f1 12 34 67 78 af d1 f2 ...
                       ^- Pointer (*data)

Aufgabe:
"Mache mir aus *data (Pointer auf #4) ein Objekt der Klasse ABC".

Ergebnis:
myobj
{
 testvar1 = 0x12;
 testvar2 = 0x34;
 testvar3 = 26488; // entspricht: 0x6778
}

Ist es irgendwie möglich? Bietet C/C++ eine Möglichkeit "mit Gewalt" 
irgendeinen Speicherbereich zu einem Objekt zu casten? Ist einfach nur 
rein Interesse halber ... ich finds immer wieder schön, was für 
Schweinereien C so alles zulässt ;)

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Der "C Weg":

ABC * classPointer = (ABC *)data;
classPointer->testvar1 = 4;

Der "C++ Weg":

ABC * classPointer = reinterpret_cast<ABC *>(data);
classPointer->testvar1 = 4;

Matthias

von Uhu U. (uhu)


Lesenswert?

Generell ist es keine gute Idee, einen Pseudo-Kopierkonstruktor - darum 
handelt es sich hier - mit void * - Parameter zu bauen.

Die etwas bessere Lösung ist ein normaler Kopierkonstruktor:

   ABC::ABC(ABC const &data)

und casten des void-Pointers beim Aufruf auf ABC * :

   ABC *pABC = new ABC(*(ABC *) void_ptr);

Die allerbeste Lösung ist der Kopierkonstruktor und korrekte Typisierung 
des Speicherbereiches, der kopiert werden soll - es gibt keinen Grund, 
das nicht zu tun und erspart im Zweifelsfall viel Ärger, der durch 
solchen Murks mit void-Pointern zustandekommen kann.

Statt dem C-cast sollte man auch einen C++ - cast verwenden.

von mr.chip (Gast)


Lesenswert?

Kann man eigentlich sicher sein, dass der Compiler aus der gleichen 
Klassendefinition immer dieselbe Reihenfolge an Bytes im Speicher 
erzeugt?

class ABC
{
public:
 unsigned char testvar1;
 unsigned char testvar2;
 unsigned int testvar3;
}

Steht danach im Speicher immer das Array {testvar1, testvar2, 
testvar3} oder könnte es auch sein, dass die Reihenfolge geändert wird 
oder dass noch andere Werte dazwischen gespeichert werden? {testvar3, 
testvar2, variable hans, testvar1}?

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

es wird vom C++ Standard garantiert das die Membervariablen in der 
Reihenfolge im Speicher liegen in der sie deklariert sind. Es kann aber 
vorkommen das esLöcher gibt -> Alignment.

Matthias

von Uhu U. (uhu)


Lesenswert?

> Kann man eigentlich sicher sein, dass der Compiler aus der leichen
> Klassendefinition immer dieselbe Reihenfolge an Bytes im Speicher erzeugt?

Die Reihenfolge ist immer die in der Definition angegebene Reihenfolge - 
wie in C auch.

Allerdings spielt das Alignment eine wichtige Rolle bei der Festlegung 
der Relativadressen der Komponenten:

Bei Alignement 2 z.B., beginnen die Komponenten immer auf geraden 
Adressen, was ggf. durch 'Padding Bytes' erzwungen wird.

Bei gleichem Alignement erzeugt der Compiler aus einer bestimmten 
Definition immer dasselbe Speicherlayout - anderenfalls wären Typecasts 
ziemlich sinnlos.

von Andreas K. (a-k)


Lesenswert?

In C++ sollte man nicht blind darauf vertrauen, dass das erste Element 
eines Objekts an Offset 0 liegt. Spätestens bei der ersten virtual 
member funktion ist das nicht mehr der Fall. Und bei multiple 
inheritance kann auch die Reihenfolge von Elementen durcheinander 
geraten.

M.a.W: Finger weg von solchen Spielchen in C++. Ist eine Zeitbombe die 
irgendwann hochgeht.

von Uhu U. (uhu)


Lesenswert?

> M.a.W: Finger weg von solchen Spielchen in C++. Ist eine Zeitbombe die
> irgendwann hochgeht.

Das stimmt so nicht:
Murksereien in C++ werden mit bösen Fehlern bestraft. Bei C++ muß man 
sich so einigen in C völlig üblichen Pfusch abgewöhnen. Insbesondere 
Typecasts sollte man meiden, wo immer möglich.

Die wichtigste Regel für C++ - Programmierer:

     Strikte Einhaltung der C++ - Regeln.

Dann ist C++ eine sehr mächtige und zuverlässige Sprache.
Anderenfalls sitzt man wirklich auf einer Zeitbombe. Das ist aber nicht 
ein Mangel der Sprache, sondern in aller Regel mangelhafte Beherrschung 
von C++.

Kleiner Tipp für den Umstieg von C auf C++: Datenstrukturen, die auch 
ein C-Compiler versteht, verhalten sich in C und C++ gleich.

von Alexander I. (daedalus)


Lesenswert?

Hallo,
da kam ja schon einiges an Rückmeldungen. Also der Hintergrund ist der 
folgende: Über einen seriellen Datenkanal tröpfeln die Bytes in einen 
unsigned char-Puffer. Dabei entsprechen 32 Bytes immer einem Objekt 
einer Klasse. Im Puffer kann immer nur ein Paket sein. Nun muß ich aus 
dieser 32 Byte langen Datenwurst ein Objekt einer Klasse erzeugen. Mit 
meiner Konstruktor-Methode klappt das prima, wie das mit einem 
Kopierkonstruktor funktionieren soll, müsste man mir noch erklären? Also 
das Konstrukt an sich ist klar, aber wie weise ich den Variablen dann 
die Werte zu? Ein weiterer Gesichtspunkt ist es Speicherplatz zu sparen, 
deshalb werden fast ausschließlich Nutzdaten (3-4 Steuerbytes) 
übertragen.

von Uhu U. (uhu)


Lesenswert?

In solchen Fällen bietet sich an, eine Basisklasse mit den allen 
Varianten gemeinsamen Daten zu definieren, und für jede Variante von der 
Basisklasse einen Typ abzuleiten, der die zusätzlichen Daten beschreibt. 
(Natürlich keine virtuelle Basisklasse!)

Das setzt allerdings voraus, daß es gemeinsame Headerdaten gibt.

Wenn dann Daten ankommen, legt man zumächst einen Pointer der 
Basisklasse auf den Anfang des Puffers, stellt fest, was es für ein Typ 
ist und castet anschließend den Basisklassenzeiger in den passenden 
abgeleiteten Zeigertyp um.

Dann hat man einen Zeiger auf den richtigen Datentyp, mit dem man 
weiterarbeiten kann.

von Andreas K. (a-k)


Lesenswert?

Uhu Uhuhu wrote:

>> M.a.W: Finger weg von solchen Spielchen in C++. Ist eine Zeitbombe die
>> irgendwann hochgeht.
>
> Das stimmt so nicht:

Worin bitte weicht deine Aussage von meiner ab? Ich schrieb "Finger weg" 
von solchen Typecasts, von Annahmen über Speicherlayouts etc, du 
schreibst "keinen Pfusch".

von Uhu U. (uhu)


Lesenswert?

@ Andreas Kaiser
Bisher war nicht die Rede von virtuellen Funktionen oder multiple 
Inheritance -- mit gutem Grund, denn das macht im Kontext des OP auch 
überhaupt keinen Sinn.

Und daraus dann auch noch den 'Ratschlag' abzuleiten, 'die Finger davon 
zu lassen', ist - milde ausgedrückt - kontraproduktiv.

Im übrigen gibt es nur eine Ausnahme von der Regel, daß Basisklassen in 
der Reihenfolge ihrer Nennung im Speicher stehen: Wenn virtuelle 
Basisklassen im Spiel sind.

Praktisch spielt diese Ausnahme aber keine Rolle, weil dieses 
Irrsinnskonstrukt so gut wie nie verwendet wird.

von Andreas K. (a-k)


Lesenswert?

> Und daraus dann auch noch den 'Ratschlag' abzuleiten, 'die Finger davon
> zu lassen', ist - milde ausgedrückt - kontraproduktiv.

Ich glaube ich verstehe, wo das Missverständnis liegt. Er sollte nicht 
die Finger von C++ lassen, sondern von bösen Spielchen wie den gezeigten 
casts. Und da sind wir wohl nicht weit auseinander.

Ich benutze ja selber gern C++ für Controller.

von Alexander I. (daedalus)


Lesenswert?

Hallo,

also die Variante mit dem Kopierkonstruktor wie oben beschrieben hab ich 
hinbekommen, es funktioniert. Sieht auch zugegebenermaßen etwas schöner 
aus, wenn man nicht irgendwelche Pointer addieren muß, sondern direkt 
die Attribute des "Originals" angeben kann.

Vielen Dank.

von Klaus F. (kfalser)


Lesenswert?

Gibt es nicht in der boost library eine Klasse zum serialisieren von 
Daten?
Suche mal mit Google nach "serialization" und "boost".

Klaus

von Uhu U. (uhu)


Lesenswert?

> Gibt es nicht in der boost library eine Klasse zum serialisieren von
> Daten?

Falls es sich um ein µC-Projekt handelt, würde ich davon abraten. Das 
gibt leicht eine Codeinflation.

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.