Forum: Mikrocontroller und Digitale Elektronik 4 Bytes zu long int konvertieren


von Markus W. (kornbanane)


Lesenswert?

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:
1
unsigned long int var=0;
2
unsigned char var1=0;
3
unsigned char var2=0;
4
unsigned char var3=0;
5
unsigned char var4=0;
6
7
  var=0x89ABCDEF;
8
  
9
  puts("\nLong Int in 4 Bytes aufsplitten: \n",0);
10
11
  var1=(unsigned char)var;
12
  var2=(unsigned char)(var>>8);
13
  var3=(unsigned char)(var>>16);
14
  var4=(unsigned char)(var>>24);
15
  
16
  puts("\nAusgabe der Variable var1:",0);
17
  puthex((unsigned long)var1,2,0);
18
  puts("\nAusgabe der Variable var2:",0);
19
  puthex((unsigned long)var2,2,0);
20
  puts("\nAusgabe der Variable var3:",0);
21
  puthex((unsigned long)var3,2,0);
22
  puts("\nAusgabe der Variable var4:",0);
23
  puthex((unsigned long)var4,2,0);
24
  
25
  puts("\n Wieder zusammensetzen: \n",0);
26
  
27
  var=0;
28
  var=(unsigned long)var1+(var2<<8)+(var3<<16)+(var4<<24);
29
//  var=(unsigned long)var1|(var2<<8)|(var3<<16)|(var4<<24);
30
  
31
  var1=(unsigned char)var;
32
  var2=(unsigned char)(var>>8);
33
  var3=(unsigned char)(var>>16);
34
  var4=(unsigned char)(var>>24);
35
  
36
  puts("\nAusgabe der Variable var1:",0);
37
  puthex((unsigned long)var1,2,0);
38
  puts("\nAusgabe der Variable var2:",0);
39
  puthex((unsigned long)var2,2,0);
40
  puts("\nAusgabe der Variable var3:",0);
41
  puthex((unsigned long)var3,2,0);
42
  puts("\nAusgabe der Variable var4:",0);
43
  puthex((unsigned long)var4,2,0);

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
1
  var=(unsigned long)var1+(var2<<8)+(var3<<16)+(var4<<24);

liegen, aber ich begreife nicht wo :(

von Achim_42 (Gast)


Lesenswert?

probier mal:
1
var=(unsigned long)var1+((unsigned long)var2<<8)+((unsigned long)var3<<16)+((unsigned long)var4<<24);

von Karl H. (kbuchegg)


Lesenswert?

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!

: Bearbeitet durch User
von Dingens23 (Gast)


Lesenswert?

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.

von Markus W. (kornbanane)


Lesenswert?

@ 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 ...

von HildeK (Gast)


Lesenswert?

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)

von hicks (Gast)


Lesenswert?

Vielleicht funktioniert das:

union
{
    struct
    {
        unsigned char var1;
        unsigned char var2;
        unsigned char var3;
        unsigned char var4;
    };
    unsigned long int var = 0;
}Variable;

von Dingens23 (Gast)


Lesenswert?

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.

von Stefanus (Gast)


Lesenswert?

> 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.

von Markus W. (kornbanane)


Lesenswert?

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 ;)

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Sean G. (atmega318)


Lesenswert?

Gleiches Problem vor 2 Tagen. Das war die (meiner Meinung nach) best 
Lösung:
var = *((uint32_t*)(var));

von Markus W. (kornbanane)


Lesenswert?

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 ;)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Achim_42 (Gast)


Lesenswert?

Ich find die Union-Lösung auch am elegantesten.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Wilhelm F. (Gast)


Lesenswert?

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?

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.