Forum: Mikrocontroller und Digitale Elektronik long in byte-Array ablegen


von Gast (Gast)


Lesenswert?

Hallo Leute,

ich nutze einen ATMEGA und wollte nur mal eure Meinung zu Folgendem 
hören.
Kann ich davon ausgehen, dass
1
  unsigned char b[128];
2
  unsigned long k = 123456;
3
4
  *(b++) = k>>24;
5
  *(b++) = k>>16;
6
  *(b++) = k>>8;
7
  *b = k;
und
1
  *((unsigned long*)b) = k;
bei diesen Controllern das Gleiche bewirken?
Der zweite Code wäre dann ja um einiges schneller und besser.
Umgekehrt würde um k wieder zu kriegen dann ja auch
1
  k=*((unsigned long*)b);
anstatt
1
  k = ((unsigned long) *(b++))<<24;
2
  k |= ((unsigned long) *(b++))<<16;
3
  k |= ((unsigned long) *(b++))<<8;
4
  k |= (unsigned long) *(b++);
gelten.
Sehe ich das richtig?

Vielen Dank im Voraus...

von Gerhard (Gast)


Lesenswert?

Hallo

wie wärs mit:

union CHG{
      unsigned char c[4];
      unsigned long k;
};

union CHG chg;
unsigned char b[128];
int i;

chg.k = 123456;

b[i++] = k[0];
b[i++] = k[1];
b[i++] = k[2];
b[i++] = k[3];

Gerhard

von Gerhard (Gast)


Lesenswert?

Sorry, muss natürlich so heissen:


union CHG{
      unsigned char c[4];
      unsigned long k;
};

union CHG chg;
unsigned char b[128];
int i;

chg.k = 123456;

b[i++] = c[0];
b[i++] = c[1];
b[i++] = c[2];
b[i++] = c[3];

Gerhard

von Gast (Gast)


Lesenswert?

@ Gerhard
das das möglich ist, sehe ich ein.
Sollte aber
*( (unsigned long*)b ) = k;
nicht auch das Gleiche bewirken, ohne viel Code schreiben zu müssen?

von Gerhard (Gast)


Lesenswert?

Das, sollte mit cast funktionieren:

unsigned char b[128];
unsigned long k = 123456;

  *(b++) = (unsigned char)k;
  *(b++) = (unsigned char)k>>8;
  *(b++) = (unsigned char)k>>16;
  *b = (unsigned char)k>>24;

Dabei nimmt der Compiler immer das lowbyte und schreibts in das Array.

aber
*( (unsigned long*)b ) = k;
was ist das ?
b ist ein char-array.
Schreib doch deinen Code mal in einen C-Compiler und schau wa der draus 
macht.

wieso kein long-array definieren ?

unsigned long b[128];
  unsigned long k = 123456;

  b[i++] = k;

gerhard

von Roland P. (pram)


Lesenswert?

ich denk mal, selbst wenn man es mit Bitshifts macht, dass das der 
Compiler schon checkt und entsprechend optimiert.

evtl geht auch memcpy(&b[i],&k,4)

von Peter D. (peda)


Lesenswert?

Gast wrote:

> Kann ich davon ausgehen, dass
>
1
  unsigned char b[128];
2
>   unsigned long k = 123456;
3
> 
4
>   *(b++) = k>>24;
5
>   *(b++) = k>>16;
6
>   *(b++) = k>>8;
7
>   *b = k;
> und
>
1
  *((unsigned long*)b) = k;
> bei diesen Controllern das Gleiche bewirken?

Nö.

Im oberen Code speicherst Du das höchstwertige Byte immer zuerst.

Im unteren Code ist es compilerabhängig, was zuerst gespeichert wird.


Wenn es darum geht, Daten byteweise zu übertragen, nehme ich 
grundsätzlich die compilerunabhängige Version, auch wenns minimal länger 
dauert.

Wenns aber nur im EEPROM gespeichert und wieder ausgelesen werden soll, 
dann isses wurscht.


Peter

von Gast (Gast)


Lesenswert?

Danke für die vielen Antworten bis jetzt!

@Gerhard
>aber
>*( (unsigned long*)b ) = k;
>was ist das ?
>b ist ein char-array.
Stimmt nicht ganz. b ist Pointer auf den Anfang eines char-Arrays, er 
zeigt also auf das erste Byte des Arrays. Da es ein "unsigned 
char*"-Pointer ist, zeigt er immer nur auf ein Byte. Durch den Cast nach 
(unsigned long*) entsteht ein Pointer mit der Adresse von b, der aber 
auf den Anfang eines 4-Byte-Langworts zeigt. Die äußere Klammer "*(...)" 
bedeutet dann: Schreibe in das Langwort auf das der Pointer zeigt den 
Wert von k.
Der Compiler schluckt das ohne Probleme.

@Roland Praml
>ich denk mal, selbst wenn man es mit Bitshifts macht, dass das der
>Compiler schon checkt und entsprechend optimiert.
Habs mit beidem versucht und mir den Assembler-Code angeschaut. Der 
AVR-GCC-Code ist mit den Shifts länger und langsamer. Da ich die Shifts 
an mehreren Stellen brauche, macht das schon einiges aus.

@Peter Dannegger
>Im unteren Code ist es compilerabhängig, was zuerst gespeichert wird.
Sorry, klar. Ich nutze den AVR-GCC-Compiler, der soweit ich weiß im 
Little Endian-Format speichert. Der Code ist für Speicher (z.B. 
Speicherkarten) gedacht, der nur in Sektoren adressierbar ist, sodass 
immer nur komplette Blöcke abgespeichert werden können. Ich suche dafür 
nach Möglichkeiten Variablen möglichst effizient und schnell ablegen zu 
können. Um die "Byteordnung" mache ich mir also nicht so viele Gedanken. 
Es ging mir vielmehr darum ob man sowas wie
1
*((unsigned long*)b) = k;
 immer so ohne weiteres machen darf, oder ob ich etwas was übersehen 
habe.

Wenn also alle Architekturen die ich verwende "Little Endian" sprechen, 
wäre der Code mit den Pointer-Casts vorzuziehen und kann bedenkenlose 
verwendet werden???

Vielen Dank, liebe Leute.

von T.Stütz (Gast)


Lesenswert?

kleiner Nachtrag @Gast

>Wenn also alle Architekturen die ich verwende "Little Endian" sprechen,
>wäre der Code mit den Pointer-Casts vorzuziehen und kann bedenkenlose
>verwendet werden???

Es gibt Architekturen (Prozessoren) die nehmen es einem sehr übel
wenn mann an eine ungerade Adresse mit Wort/Doppelwort Zugriffen 
hinlangt. Bsp C167.

Bei einer Variablen "unsigned char ucZeichen;" istaber nicht immer 
gewährleistet das selbige an einer geraden Adresse ist.

mfG
T.Stütz

von Gast (Gast)


Lesenswert?

@T.Stütz
Stimmt, daran habe ich nicht gedacht. Weiß jemand den wie das bei den 
ATMEGAs ist?
Denn dann habe ich jetzt aber ein Problem! Ich würde ja prinzipiell auch 
mit den Shifts arbeiten, muss aber z.B. auch Floats in den Speichern 
ablegen. Da darf ich aber keine Shifts machen (Compiler: "invalid 
operands to binary >>"). Da ein Float 4 Byte hat müsste ich ihn erst 
z.B. per "(long*)" in etwas casten, was ich shiften darf, das per Shift 
in das Byte-Array ablegen und dann nach dem Auslesen wieder auf "float*" 
zurückcasten. Ob das so gut ist???

Wie wärs den tatsächlich mit "memcpy"? Da hätte ich zwar auch keinen 
Einfluss auf die "Byteorder", das Problem mit den ungeraden Adressen 
hätte sich aber dann erledigt.

von Willi W. (williwacker)


Lesenswert?

Mal was anderes:

Abgesehen davon ob es "tut" (Little oder Big Endian oder was auch 
immer), es ist auch eine Stilfrage. Meiner Meinung nach sollte der Code 
so programmiert sein, dass er selbsterklärend ist und man damit auch 
möglichst ohne Kommentare auskommt.

Die Variante mit der Union würde dem entsprechen.

Die andere Variante, in die 4 Byte heute 4 chars, morgen 2 Integers und 
übermorgen ein Long reinzuschreiben, nicht! Ein solcher Code ist erst 
einmal verwirrend und MUSS kommentiert werden.

Eleganter ist das auch nicht, ein guter Compiler bekommt die 
union-Variante mit gleichem Byte-Aufwand hin. Selbst wenn nicht, die 
Übersicht hat gewonnen, damit ist der Code leichter zu pflegen und damit 
sinkt der spätere Aufwand für die Pflege. Bereits bei der Programmierung 
sollte man daran denken.

Bevor Ihr mich jetzt steinigt, einfach mal so meine Meinung nach der ich 
seit etlichen Jahren in allen möglichen Sprachen arbeite.

Nicht immer erfolgreich, wie ich zugeben muss. Code so zu schreiben ist 
nicht einfach. So wie ich finde, dass das Kommentieren eines Codes 
schwieriger ist, als die eigentliche Programmiererei.

Schöne Pfingsten

Willi Wacker

von Peter D. (peda)


Lesenswert?

Willi Wacker wrote:
> Meiner Meinung nach sollte der Code
> so programmiert sein, dass er selbsterklärend ist und man damit auch
> möglichst ohne Kommentare auskommt.


Also für "Selbsterklärung" kann ich mir nun aber überhaupt nichts 
kaufen.

Was nützt mir die schönste Erklärung, wenn sie falsch ist !


Ich bevorzuge daher immer die weniger Fehler provozierende Variante.

Wenn sie weniger "schön" ist, kann man sie ja in ein Macro verpacken.

Wenn ich also vermeiden kann, mir die Edianes des Compilers ansehen zu 
müssen, dann mach ich das auch.


Peter

von Gast (Gast)


Lesenswert?

Also ich habs jetzt jeweils mit
1
memcpy(b,&k,4);
gemacht. Das Ergebis ist bisher das Gleiche wie bei der Pointer-Variante 
(sogar die Codegröße ist gleich ;-) ) nur das ich somit hoffentlich die 
eventuellen Schwierigkeiten mit gerader/ungerader Adresse umgangen habe.
Bis jetzt läufts mal...

von Gast (Gast)


Lesenswert?

Nachtrag @Peter Dannegger
>Wenn ich also vermeiden kann, mir die Edianes des Compilers ansehen zu
>müssen, dann mach ich das auch.
Verstehe ich vollkommen! Nur brauche ich leider auch Floats und da darf 
ich kein Shift machen.

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.