Forum: PC-Programmierung Cast Inkrement problem


von Gast (Gast)


Lesenswert?

hallo,

ich wollte einen DatenStrom den ich aus einem EEProm lese folgendermaßen 
verarbeiten:

  void* ptr = ((void*)data);

  frame->numNodeIDs   =  ((BYTE*)ptr)++;
  frame->nodes    =  ((BYTE*)ptr)++;
  frame->cmdClass    =  ((BYTE*)ptr)++;
  frame->cmd    =  ((BYTE*)ptr)++;
  frame->dataLen    =  ((UINT*)ptr)++;
  frame->data    =  ((BYTE*)ptr)++;

aber der compiler meint expression must be a modifiable lvalue.

leuchtet mir nicht ein warum es nicht so gehen sollte denn ptr ist doch 
nicht const oder so was?

Gruß

flow

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Warum verwendest Du nicht von vornherein einen BYTE-Pointer?

von Martin L. (melvin_the_moose)


Lesenswert?

>   void* ptr = ((void*)data);
>
>   frame->numNodeIDs   =  ((BYTE*)ptr)++;
>  ...
++ ist ein Operator, der auf eine Variable (lvalue) angewandt werden 
muß. ((BYTE*)ptr) ist aber ein Zwischenrechenergebnis.
Der nächste Schritt wäre, ((BYTE*)ptr++); zu schreiben. Aber ich fürchte 
(bin aber nicht sicher...), daß das auch nicht geht, weil ptr ein void 
pointer ist.
Für die Zeigerarithmetik ptr++ müßte die Größe des Datentyps bekannt 
sein, auf den ptr zeigt. Aber was ist sizeof(void) (0 oder was?)?

Gruß

Martin

von Chris (Gast)


Lesenswert?

((BYTE*)ptr) gibt dir einen r-value vom Typ BYTE*, aber ++ verlangt 
einen l-value. In C++ kannst du schreiben
((BYTE*&)ptr)++;

Oder in C (evtl., das ist ungetestet):
(*(BYTE**)ptr)++;

Aber ich schliesse mich Rufus an: Wieso machst du das so umstaendlich?

von yalu (Gast)


Lesenswert?

> Warum verwendest Du nicht von vornherein einen BYTE-Pointer?

Er möchte offensichtlich aus dem Array nicht nur BYTE-, sondern auch
UINT-Werte auslesen. Bei letzteren muss ptr um 4 statt um 1 erhöht
werden. Wenn man Lesen und Inkrementieren in einen Ausdruck pressen
will kommen solche seltsamen Konstrukte zustande.

> (*(BYTE**)ptr)++;

Genau so geht's, nur das & vor ptr fehlt:
1
  frame->numNodeIDs   =  *(*(BYTE **)&ptr)++;
2
  //...
3
  frame->dataLen      =  *(*(UINT **)&ptr)++;

Eine gecastete Variable ist kein lvalue, ein dereferenzierter
gecasteter Pointer hingegen schon. Das ist der Trick bei der Sache.

Damit das Ganze nicht so wüst aussieht, kannst du es in ein Makro
packen:
1
#define READ_N_INC(p,t) (*(*(t **)&p)++)
2
  // ...
3
4
  frame->numNodeIDs   =  READ_N_INC(ptr, BYTE);
5
  //...
6
  frame->dataLen      =  READ_N_INC(ptr, UINT);

von Rolf Magnus (Gast)


Lesenswert?

> frame->numNodeIDs   =  ((BYTE*)ptr)++;

Im Prinzip gibt dir der Cast eine Kopie von ptr vom TYP BYTE* zurück. 
Diese Kopie versuchst du zu inkrementieren. Da sie nur ein temporärer 
Zwischenwert ist, der nicht modifizierbar ist, meldet der Compiler einen 
Fehler. Wenn das gehen würde, hätte es aber eh nicht den gewünschten 
Effekt, da ptr unverändert bliebe.

> frame->dataLen      =  *(*(UINT **)&ptr)++;

Eine andere Möglichkeit, wenn man ptr als BYTE* definiert und nicht 
alles in einen einzelnen Audsruck quetscht:

frame->dataLen    =  (UINT*)ptr; ptr += sizeof(UINT);

Für die ganzen anderen Einträge, bei denen eh ein BYTE* verlangt wird, 
kann man sich das Ganze dann auch gleich schenken.

von Gast (Gast)


Lesenswert?

Hallo,
sry für die späte antwort... Aber vielen Dank für die sehr guten und 
schnellen antworten. hat wunderbar geklappt und hab wieder ein bischen 
mehr ahnung ;)

Gruß

von 900ss (900ss)


Lesenswert?

Rolf Magnus wrote:
>> frame->dataLen      =  *(*(UINT **)&ptr)++;

Hier verstehe ich, warum es Leute gibt, die sagen das die Sprache C/C++ 
einfach Müll ist. Weil man eben solche Ausdrücke erzeugen kann.

Bin gespannt ob der Programmierer die selber nach einem Jahr oder später 
selber noch versteht. Unleserlicher geht es kaum noch.

> frame->dataLen    =  (UINT*)ptr; ptr += sizeof(UINT);

Das ist die einzige Lösung, die akzeptabel ist.

von Rolf Magnus (Gast)


Lesenswert?

> Hier verstehe ich, warum es Leute gibt, die sagen das die Sprache C/C++
> einfach Müll ist. Weil man eben solche Ausdrücke erzeugen kann.

Man kann in jeder Programmiersprache schwer verständlichen Code 
schreiben.

von 900ss (900ss)


Lesenswert?

Rolf Magnus wrote:
>> Hier verstehe ich, warum es Leute gibt, die sagen das die Sprache C/C++
>> einfach Müll ist. Weil man eben solche Ausdrücke erzeugen kann.
>
> Man kann in jeder Programmiersprache schwer verständlichen Code
> schreiben.
Das stimmt. Allerdings in C/C++ auch zusätzlich noch sehr kryptisch 
(s.o.). Das hat mit gutem Programmierstil nichts mehr zu tun.

von Karl H. (kbuchegg)


Lesenswert?

900ss D. wrote:
> Rolf Magnus wrote:
>>> Hier verstehe ich, warum es Leute gibt, die sagen das die Sprache C/C++
>>> einfach Müll ist. Weil man eben solche Ausdrücke erzeugen kann.
>>
>> Man kann in jeder Programmiersprache schwer verständlichen Code
>> schreiben.
> Das stimmt. Allerdings in C/C++ auch zusätzlich noch sehr kryptisch
> (s.o.). Das hat mit gutem Programmierstil nichts mehr zu tun.

Stimme ich dir zu.
Die Schlussfolgerung kann aber eigentlich nur lauten:
Da zäumt jemand ein Pferd mit dem falschen Werkzeug auf.
Anstatt diesen ganzen castings eine Struktur definieren,
die Struktur mit einer Union über den Bytebuffer legen
und schon verschwindet das Problem ganz von alleine.
Oder aber gleich aus dem EEPROM in eine derartige Struktur
variable einlesen und auch dann ist das Problem ganz von alleine
aus der Welt.

Manchmal sind casts notwendig. Wenn man aber exzessiv casten
muss um ein Problem zu lösen, sollte man mal einen Schritt
zurücktreten und sich überlegen, ob man im Moment mit den
ganzen Casts nicht das falsche Problem unter den Casts versteckt.

von Morin (Gast)


Lesenswert?

> Anstatt diesen ganzen castings eine Struktur definieren,
> die Struktur mit einer Union über den Bytebuffer legen
> und schon verschwindet das Problem ganz von alleine.

Wie würdest du das denn machen? Per union zwei Datentypen 
"übereinanderlegen" ist klar, aber wie würdest du die Struktur hinter 
dem Pointer in einem C Datentyp ausdrücken? Dafür gibt es in C keinen 
Datentyp (es wird zwar immer wieder fälschlich behauptet, struct-Typen 
wären dafür gut, aber bei denen fügt der Compiler Füllbytes ein, unter 
anderem um 32-bit-Variablen zu alignen, und dann stimmen sie nicht mehr 
mit der tatsächlichen Struktur der Daten überein).

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> aber bei denen fügt der Compiler Füllbytes ein

... wenn man es denn nicht abschaltet, was je nach Compiler mit 
verschiedenen #pragmas erfolgen kann.
Und dann kann so eine Struktur sehr wohl funktionieren.
Allerdings muss man sich dann als Programmierer auch damit 
auseinandersetzen, wie die verwendete Hardware auf Zugriffe auf nicht 
korrekt ausgerichtete Objekte verhält; nicht alle Prozessoren sind da so 
nachsichtig wie 32-Bit x86.

von yalu (Gast)


Lesenswert?

>> aber bei denen fügt der Compiler Füllbytes ein
>
> ... wenn man es denn nicht abschaltet, was je nach Compiler mit
> verschiedenen #pragmas erfolgen kann.

Ein Programm, dessen Funktion von solchen #pragmas abhängt, ist aber
in meinen Augen kein richtiges C-Programm mehr, da es stark compiler-
und hardwareabhängig ist.

Der Trick mit den Pointern (egal, ob die kryptische oder weniger
kryptische Variante genommen wird) ist wenigstens nur
hardwareabhängig.

Die einzig wirklich saubere Lösung wäre es, die Werte mittels Shift-
und Oder-Operationen zusammenzusetzen.

von Karl H. (kbuchegg)


Lesenswert?

yalu wrote:
>>> aber bei denen fügt der Compiler Füllbytes ein
>>
>> ... wenn man es denn nicht abschaltet, was je nach Compiler mit
>> verschiedenen #pragmas erfolgen kann.
>
> Ein Programm, dessen Funktion von solchen #pragmas abhängt, ist aber
> in meinen Augen kein richtiges C-Programm mehr, da es stark compiler-
> und hardwareabhängig ist.

Über einen Punkt, denke ich, gibt es hier soweiso keine Dikussion:
Derartige Umbiegereien hat man lediglich an den I/O Schnittstellen
eines Programmes. Sprich dort, wo ein externes System irgendwie
an den großen Rest eines Programmes angebunden ist.

An diesen Stellen hab zumindest ich absolut kein Problem damit,
wenn das hardware/compilerabhängig ist. Das ist für mich
in Ordnung und ist mir eigentlich lieber als diese Casting Orgien :-)
Soooo oft wechsle ich nun auch wieder nicht den Compiler.

> Die einzig wirklich saubere Lösung wäre es, die Werte mittels Shift-
> und Oder-Operationen zusammenzusetzen.

Da hast du allerdings recht.

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.