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
> 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
((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?
> 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:
> 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.
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ß
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.
> 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.
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.
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.
> 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).
> 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.
>> 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.
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.