Forum: PC-Programmierung 4 Byte Array in long umwandeln


von Timur Y. (smokingslim)


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

von Magnus Müller (Gast)


Lesenswert?

Programmiersprache?

von Rolf Magnus (Gast)


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.

von Christian R. (supachris)


Lesenswert?

Oder mit Referenzierung/Dereferenzierung in einem Schritt:
Klappt aber nur, wenn die Endieness stimmt :)
1
long value = *(long*)&Array[0];

  

von Timur Y. (smokingslim)


Lesenswert?

Ah sorry die Sprache ist C.
Diese Lösung ist mir auch schon eingefallen allerding sind das ja über 
50 Rechenoperationen das müsste doch mit Arrays oder Pointern eleganter 
gehen.

von Timur Y. (smokingslim)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


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.



von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.


von Christian R. (supachris)


Lesenswert?

Mit der Pointer-Geschichte ist es auf einem 32Bit Rechner nur eine 
32-Bit Lade-Operation.

von Timur Y. (smokingslim)


Lesenswert?

Super Danke das habe sogar ich verstanden.

von Rolf Magnus (Gast)


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.

von Sebastian (Gast)


Lesenswert?

Wie wäres hiermit ???

union Converter
{
  long  lData;       // complete data
        char  cData[4];      // Data packages
}Switch;

//Z.B.:

Switch.lData = 0x12979898;

// Zugriff
x = Switch.cData[0]

von Simon K. (simon) Benutzerseite


Lesenswert?

Sowas darf man nach C-Konvention nicht, bzw. das Ergebnis kann sich von 
Compiler zu Compiler und Zielplattform unterscheiden und hat 
möglicherweise nicht den gewünschten Effekt.

von Rufus Τ. F. (rufus) Benutzerseite


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

von yalu (Gast)


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.

von MNR (Gast)


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

von Christoph _. (chris)


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

von Rolf Magnus (Gast)


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

?

von MNR (Gast)


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

von Edis (Gast)


Lesenswert?

Hast es hingekriegt oder soll ich vorbeikommen?  ;-)
Gruß Edis

von Edis (Gast)


Lesenswert?

Hat es hingekriegt oder soll ich vorbeikommen? ;-)
Gruß Edis

von Timur Yigit (Gast)


Lesenswert?

Ha ne danke ich habs jetzt geschoben damit der coder übertragbar bleibt

tschu

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.