Hallo Leute,
ich weiß, das Thema gabs schon öfters hier, deshalb bin ich auch so
verwirrt weshalb ichs nicht hinbekomme - stweh voll aufm Schlauch ...
Ich will in c aus 4 unsigned Bytes ein unsigned long int erzeugen.
Hier dazu der mein Code:
Wie ihr seht hab ich beim "zusammensetzen" der long int variable "var"
zwei Varianten ausprobiert. Diese führen beide zum gleichen Ergebniss
...
Die Ausgabe auf dem Terminal am PC sieht so aus:
1
Long Int in 4 Bytes aufsplitten:
2
3
Ausgabe der Variable var1:0xEF
4
Ausgabe der Variable var2:0xCD
5
Ausgabe der Variable var3:0xAB
6
Ausgabe der Variable var4:0x89
7
8
Wieder zusammensetzen:
9
10
Ausgabe der Variable var1:0xEF
11
Ausgabe der Variable var2:0xCD
12
Ausgabe der Variable var3:0xFF
13
Ausgabe der Variable var4:0xFF
Also am Anfang splitte ich die long int variable in 4 Bytes auf, was
laut Terminalausgabe auch richtig funktioniert.
Dann fasse ich die 4 Bytes wieder zu dem long int zusammen (und hier
muss was schief laufen), dann splitte ich die long int Variable wieder
in 4 Bytes auf und gebe diese aus. Wie man sieht stimmt der Wert für
var3 und var4 nicht überein. Da die Ausgabefunktionen und der Ablauf bei
beiden Ausgaben der gleiche ist - also die Terminalausgabe vor dem
Zusammensetzen und nach dem Zusammensetzen, kann der Fehler ja nur in
der Zeile
Wenn du die Einzelbytes wieder zusammensetzt, musst du darauf achten,
dass ein
1
var2<<8
zuallererst mal eine Operation ist, die im Datentyp 'int' durchgeführt
wird.
Dies deshalb, weil var2 bei dir ein unsigned char ist.
Gerechnet wird immer im höchsten Datentyp, der in einer Operation
beteiligt ist, bei dir wäre das dieser unsigned char, aber nicht kleiner
als int. Damit ist das eine int Operation. Solange du nur um 8 Bit
schiebst, ist das (abgesehen vom Vorzeichen) ok, aber spätestens wenn du
um 16 Bit schiebst, schiebst du deinen kompletten 8 Bit Wert aus dem 16
Bit Werte Bereich eines int nach links raus.
Du musst mehr auf die Datentypen, bzw. auf die Regeln, wie diese auf die
Auswahl der Operationen einwirken achten!
Hallo,
auf welchem "Ziel" machst du das? µC oder PC?
Ich könnte mir folgende Ursache vorstellen:
Das Shiften über die Wortbreite des Ziels ist nicht definiert. Bei einem
ATMEGA sind das glaube ich 8 bit, beim PC 32 oder 64. Shiftet man
weiter, ist das Ergebnis undefiniert.
Ich hatte das bei einem PIC24 einmal, das war das Ergebnis ähnlich wie
bei dir - alles war über >>16 hinausging war immer 0xFF. Grund war, dass
der PIC24 ein 16bitter ist.
"Gelöst" habe ich das dan recht unelegant mit Multiplikation/Division
statt shiften.
Wahrscheinlich gibt es aber viel elegantere Lösungen.
@ Karl Heinz:
Ich glaube ich hab so halb verstanden was du meinst, aber wie soll ich
es denn sonst machen ? Vielleicht erst var1 und var2 laden und dann die
long int Variable einmal nach links shiften lassen ??
@Dingens23:
Es handelt sich um einen Fujitsu µC. Und zwar ein 16 Bitter, das würde
ja dann auch zu deinem Problem mit dem PIC passen. Aber ich versteh es
nicht wieso es nicht geht. WIeso kann ich mit nem 16 Bitter nicht x
beliebige Stellen schieben aber mit einem 8 Bitter ...
Markus Wi*** schrieb:> Vielleicht erst var1 und var2 laden und dann die> long int Variable einmal nach links shiften lassen ??
Das ist imho eine ganz brauchbare Möglichkeit. Ausführlich also etwa so:
var = (unsigned long) var4;
var = var <<8;
var += (unsigned long) var3;
var = var <<8;
usw.
(Keine Garantie, bin kein SW-Mensch)
Hallo,
eine Quelle für das undefinierte Verhalten habe ich auch nicht.
Ich erkläre mir das so:
Bei deinem µP sind die Register 16bit breit. Shiften kann er nur von
einem Register in das nächste. Wenn man weiter als 16bit shiftet, dann
hat das Ergebnis im Zielregister keinen Platz mehr - dan wird irgendwas
(vermutlich HIGH) hereingeshiftet.
Eine mögliche Lösung wäre folgende:
uint8_t var[4];
uint32_t var2;
var2 = var[0];
var2 = var2 * 256; //entspricht var2<<8
var2 =var2 + var[1];
var2 = var2 * 256;
var2 =var2 + var[2];
var2 = var2 * 256;
var2 =var2 + var[3];
Reichlich unelegant und langsam, sollte aber funktionieren.
Man könnte noch versuchen, ob man die *256 durch <<8 ersetzen kann, das
wird aber wahrscheinlich nicht funktionieren.
> Man könnte noch versuchen, ob man die *256 durch <<8 ersetzen kann,> das wird aber wahrscheinlich nicht funktionieren.
Ich denke, das führt zu exakt dem gleichen Assembler Code.
Da es mit dem shiften nicht wirklich funktioniert hat habe ich es jetzt
ganz einfach und viel eleganter mit ner union gemacht...
[c]
union bla{
unsigned long int var;
unsigned char vars[4];
} blubb;
[c/c]
Da ja bei einer union die Speicherbereiche gleich sind, besteht ja der
long int var automatisch aus den array vars...
Vielleicht hilfts ja nochmal jemanden ;)
Markus Wi*** schrieb:> Da es mit dem shiften nicht wirklich funktioniert hat
d.h. du hast das eigentliche Problem nicht wirklich verstanden. Schade.
Das wird sich in der Zukunft noch als Bumerang erweisen.
Dabei hat Achim_42 vor mir die korrekte Lösung sogar schon gepostet und
man hätte einfach nur darüber nachdenken müssen, warum die Casts genau
dort stehen müssen, wo sie stehen.
Karl Heinz schrieb:> d.h. du hast das eigentliche Problem nicht wirklich verstanden. Schade.> Das wird sich in der Zukunft noch als Bumerang erweisen.> Dabei hat Achim_42 vor mir die korrekte Lösung sogar schon gepostet und> man hätte einfach nur darüber nachdenken müssen, warum die Casts genau> dort stehen müssen, wo sie stehen.
Ja da hast du ja schon recht ...
Habe es nochmal ausprobiert und es geht so wie Achim es geschrieben
hatte. Allerdings ist die Sache mit der Union ja schon eleganter in
meinem Fall, dann kann ich mir das shiften sparen. Trotzdem gut zu
wissen was so ein Typecast bewirken kann ;)
Markus Wi*** schrieb:> Habe es nochmal ausprobiert und es geht so wie Achim es geschrieben> hatte. Allerdings ist die Sache mit der Union ja schon eleganter in> meinem Fall, dann kann ich mir das shiften sparen.
UM das shiften brauchst du dir keine großen Sorgen machen. Denn so
dämlich sind Compiler dann auch wieder nicht, dass sie Shifts um ein
Vielfaches von 8 als tatsächlich reale Shifts umsetzen und nicht auf
Bytezugriffe.
Achim_42 schrieb:> Ich find die Union-Lösung auch am elegantesten.
Sie hat nur einen Nachteil: Du musst die Byteorder deines Systems
kennen. Die Shift-Variante ist davon befreit. Da muss man nur wissen,
welche Byteorder den Ausgangswerten zugrunde liegt.
Letzten Endes wird sich der resultierende Maschinencode nicht großartig
unterscheiden, nur eben mit dem einen Unterschied, dass sich der
Compiler in der Shift-Variante darum kümmert, in welcher Reihenfolge die
4 Bytes im Speicher angeordnet werden müssen um einen unsigned long zu
bilden.
Achim_42 schrieb:> Ich find die Union-Lösung auch am elegantesten.
Über die UNION teilte ich auf einem ARM schon mal ein Long sogar in 32
Flags bzw. auch Flaggruppen zu 3 oder 4 bit, alles was man möchte und
unterhalb 32 bit liegt, weil der ARM gar keine Flags hat wie ein 8051.
Z.B. Umportierung von C-Code von 8051 zu ARM. Das funktionierte auch
prächtig. Man kann die Flags dann auch als Long-Wert verarbeiten, z.B.
bei der Initialisierung auf einen Schlag alle Null setzen.
Allerdings geht hier der Sinn verloren, Code, RAM und Zugriffszeit auf
Flags wie beim 8051 kompakt zu halten. Ein 8051 braucht zur Bearbeitung
eines Flags (setzen, löschen, bedingter Sprung) nur ein bis zwei Byte
Code, und einen bis zwei Befehlszyklen. Der ARM macht gigantisch mehr
daraus. Aber wenn das nicht die Hauptrolle spielt?