4 Byte Array in long umwandeln

OP #579158
Lesenswert?

Hi Cracks ich hab da ein Anfänger Problem.

Ich bekomme von einem yC 4 einzelne Byte zugeschickt die aber einen 
32Bit Wert darstellen also wollte ich die einzelnen Bytes in Array 
Packen dann hab ich denn 32 Bit wert.
Mein Problem ist wie kann ich dieses Array in einen long umwandeln damit 
ich damit rechnen kann.

Danke
Gast #579169
Lesenswert?

> Mein Problem ist wie kann ich dieses Array in einen long umwandeln
> damit ich damit rechnen kann.
1
long value = (unsigned long)(byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
2
    printf("Value is %x\n", value);

Je nach Byteorder der ankommenden Daten müssen die Bytes evtl. umgekehrt 
werden.
Persönliche Seite #579203
Lesenswert?

Ein Array aus vier 8-Bit Variablen belegt im Speicher vier 
aufeinanderfolgende Bytes. Eine long-Variable belegt im Speicher vier 
aufeinanderfolgende Bytes.

Also ist es möglich, den Speicher bedarfsorientiert "anders" zu 
interpretieren, allerdings ist hier die Interpretation 
implementierungsabhängig, was an der "endianness" des Prozessors liegt.

Ein Array { 0x12, 0x34, 0x56, 0x78 } wird auf little-Endian-Prozessoren 
(u.a. Intel) als Long-Wert 0x78563412 interpretiert, auf 
big-Endian-Prozessoren (u.a. Motorola) hingegen als 0x12345678.


#579206
Lesenswert?

Timur Yigit wrote:
> long value = *(long*)&Array[0];
>
> Ich habe mich jetzt mal ein bischen in die PointerGeschichte eingelesen
> aber diese Anweisung versteh ich gar nicht. Wo wird denn gezeigt das der
> Wert Array[x] an die richtige stelle in dem long abgelegt wird.

gar nicht, das wird vorher schon gemacht
1
  unsigned char Array[4];
2

3
  Array[0] = erster_Wert_den_du bekommst;
4
  Array[1] = zweiter_Wert_den_du_bekommst;
5
  Array[2] = dritter_Wert_den_du_bekommst;
6
  Array[3] = vierter_Wert_den_du_bekommst;
7

8
  long value = *(long*)Array;

Der Trick besteht darin, dass bei einem Array die 4
Bytes im Speicher hintereinander abgelegt werden und
ein long ebenfalls aus 4 Bytes besteht.

Die letzte Anweisung, die mit dem Cast, sagt nichts
anderes als:
  * nimm die Startadresse des Arrays
    Array

  * und tu mal so, als ob diese Adresse die Adresse eines
    long wäre
    (long*)Array

  * von dieser angenommenen long-Adresse holst du jetzt
    mal den long Wert.
    *(long*)Array

Im Grunde trickst man den Compiler dahin, dass er die 4 Bytes
des Arrays als long auffasst und als long weiterbehandelt.


Allerdings muss die Reihenfolge der Bytes stimmen. Ob also
die Reihenfolge erster_Wert_..., zweiter_Wert_... etc.
richtig ist oder umgedreht werden muss, hängt davon ab in welcher
Reihenfolge der Sender die Bytes schickt und in welcher Reihenfolge
der Empfänger sie haben muss, damit wieder der korrekte Wert
herauskommt.
#579209
Lesenswert?

Timur Yigit wrote:
>>
1
>> long value = (unsigned long)(byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
2
>>

> Diese Lösung ist mir auch schon eingefallen allerding sind das ja über
> 50 Rechenoperationen

Dann hast du den Optimizer deines Compilers nicht eingeschaltet.
Das sollten nicht wesentlich mehr als 4 Operationen sein, die
allesamt Register-Ladeoperationen sind.

Gast #579274
Lesenswert?

> Diese Lösung ist mir auch schon eingefallen allerding sind das ja über
> 50 Rechenoperationen

Also bei mir sind es eher so 10 rum, was zugegebenermaßen immer noch 
lange nicht so gut ist, wie ich erwartet hätte. Aber machen dir wirklich 
50 Rechenoperationen pro per serieller Schnittstelle empfangenem Wert 
sorgen? Es ist eher unwahrscheinlich, daß das einen meßbaren Unterschied 
in der Prozessorauslastung macht.
Persönliche Seite #580695
Lesenswert?

Mit anderen Worten:

Genau so macht man es.


Und bekommt Haarausfall, wenn man das funktionierende Programm auf eine 
andere Hardware portieren will, weil man diese Stellen auch nicht im 
Quelltext als problematisch oder "hic sunt dracones" gekennzeichnet hat.

Und dann kreischt und flucht man und postet interessante Vermutungen 
über Compilerfehler in irgendwelchen Foren ...
Gast #580723
Lesenswert?

>> Diese Lösung ist mir auch schon eingefallen allerding sind das ja
>> über 50 Rechenoperationen
>
> Also bei mir sind es eher so 10 rum, was zugegebenermaßen immer noch
> lange nicht so gut ist, wie ich erwartet hätte.

Folgendes
1
long value = (unsigned long)(byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
ergibt bei mir 16 Bytes Code, aber das falsche Ergebnis, da die 16-
und 24-Bit-Shifts auf int angewandt immer 0 ergeben. Besser:
1
unsigned long value =
2
  (unsigned long)byte[0] << 24 |
3
  (unsigned long)byte[1] << 16 |
4
  (unsigned int) byte[2] <<  8 |
5
                 byte[3];
Das braucht aber schon 37 Bytes.

Die Pointer-Methode scheint wohl die Lösung zu sein, wenn es auf
Geschwindigkeit ankommt, sonst würde ich auch eher die Shift-Methode
vorziehen, weil dabei deutlicher wird, welches Byte des Bytestroms zum
High-Byte und welches zum Low-Byte wird.
Gast #580728
Lesenswert?

Die union Lösung ist genauso gut oder schlecht, wie
1
long value = *(long*)Array;
 Wenn man Hardware-unabhängig bleiben will, müßte man nur die Indizes 
des Arrays per Macro umsortieren, dann läßt sich das leicht portieren.

Gruß, Matthias
#582483
Lesenswert?

MNR wrote:
> Die union Lösung ist genauso gut oder schlecht, wie
1
long value =
2
> *(long*)Array;
 Wenn man Hardware-unabhängig bleiben will, müßte man
> nur die Indizes des Arrays per Macro umsortieren, dann läßt sich das
> leicht portieren.

Nein, um portabel zu bleiben musst du mehr machen. Ein char-Array muss 
nicht zwangsweise ausgerichtet sein im RAM. Es gibt aber CPUs, bei denen 
ein long ausgerichtet sein muss, sich also auf einer durch 4 teilbaren 
Adresse befinden muss. Auf so einem Rechner darfst du unter keinen 
Umständen ein nicht ausgerichtetes char-Array nach long casten, sonst 
stürzt das Programm einfach ab. Die Ausrichtung des char-Arrays kannst 
du mit Standard-Mitteln nicht hinbekommen.

Am sichersten ist es daher meiner Meinung nach, die Umwandlung zentral 
zu machen und eine Funktion zu schreiben:
long charsToLong(char blub[4]);

Dann hat man den mglw. unportablen Code wenigstens nur an einer Stelle. 
Ich würde aber dort einfach die Lösung mit bit-shifts nehmen. 
Schließlich geht es hier um ein PC- und nicht ein 
Mikrocontroller-Programm. Bei PC-Programmen spielt in erster Linie die 
Laufzeit-Klomplexität im Sinne der Landau-Notation eine Rolle, weniger 
die tatsächlichen konstanten Laufzeitunterschiede zwischen O(1) und O(1) 
(solange der Faktor nicht in die tausende geht, und je nach Kontext).
Gast #582567
Lesenswert?

> Mit anderen Worten:
>
> Genau so macht man es.

Ja, "man" macht viel, was eigentlich falsch ist.

> Und dann kreischt und flucht man und postet interessante Vermutungen
> über Compilerfehler in irgendwelchen Foren ...

Genau so läufts. Aber zum Glück sind wird ja hier und immer zur 
Seelsorge bereit. ;-)


@yalu:

Hast natürlich Recht. Ich weiß auch nicht, was ich dabei gedacht habe. 
Beim ersten Byte habe ich noch dran gedacht, den Cast zu machen, beim 
Rest nicht mehr.

> Besser:
> unsigned long value =
>   (unsigned long)byte[0] << 24 |
>   (unsigned long)byte[1] << 16 |
>   (unsigned int) byte[2] <<  8 |
>                  byte[3];
>
> Das braucht aber schon 37 Bytes.

Was braucht:
1
unsigned long value = byte[0];
2
value <<= 8;
3
value |= byte[1];
4
value <<= 8;
5
value |= byte[2];
6
value <<= 8;
7
value |= byte[3];

?
Gast #583243
Lesenswert?

@Chris
Wenn man auch zu diesen CPUs kompatibel sein möchhte, würde man sich ein 
long definieren, und das dann als charArray nutzen. Also z.B. sinngemäß
1
long l;
2
char cp[] = &l;
3
cp[0]= ...

Gruß, Matthias

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren