uint32 Array in uint8 Array wandeln

Gast #7197420
Lesenswert?

Hallo,

wie wandle ich am besten ein uin32 Array in ein uint8 Array am 
effizientesten (Kein Pfusch ;-) ).

Klar kann ich so was verwenden, aber vielleicht hat jemand noch andere 
gute Ideen.
1
uint8_t arr[24];
2
uint32_t Tx_PP[6];
3
arr[0]=(Tx_PP[0] >> 24) & 0xFF;
4
arr[1]=(Tx_PP[0] >> 16) & 0xFF;
5
arr[2]=(Tx_PP[0] >> 8) & 0xFF;
6
arr[3]=(Tx_PP)[0] & 0xFF;

Vielen Dank, Grüße, Seppel

Quelle: 
https://electronics.stackexchange.com/questions/523334/how-to-split-uint32-into-four-uint8-in-efr32

[Mod: c-Tags eingesetzt, beim nächsten Mal bitte selber machen]
Gast #7197461
Lesenswert?

Was möchtest Du denn machen? Oder was willst Du optimieren?


Viele Konstrukte setzt Dein Compiler bei höchste Optimierung in wenige 
oder 1 Assemblerzeile um. Genauso gut kann er aber auch daran 
verzweifeln, weil ihm irgendwas nicht passt.

Und ja, manche nehmen memcpy, unions oder was auch immer und sichern 
dass mit ein paar asserts oder Randbedingungen ab. Sauber ist das nicht 
immer, aber oft schneller oder pragmatischer, wenn ich z.B. nur einen 
pointercast brauche.
Gast #7197490
Lesenswert?

Wenn du es portabel halten möchtest, dann ist die jetzige Lösung wohl 
das geeigete. Bei allem anderem musst du den Prozessor kennen.

Von TI gibt es z.B. noch eine DSP Serie, die ist nur 16 bittig, d.h. 
auch ein uint8_t und ein Byte besteht aus 16 Bit. Und wenn du so eine 
CPU benutzt, dann sind sämtliche Unions etc nicht mehr möglich. Das 
portable Konstrukt funktioniert aber weiterhin.
#7197499
Lesenswert?

Weil es nicht zu erkennen ist, ob C oder C++:

- in C ist type-punning per union explizit ok.
- in C ist type-punning per pointer-cast auch UB wegen strict-aliasing
- in C++ ist type-punning per union immer UB.

Und: type-punning per union ist auch gar nicht notwendig, denn Deine 
Lösung ist die richtige und nicht aufwändiger als per union.
Gast #7197872
Lesenswert?

MaWin schrieb:
> Wäre mir neu. Ist das in einem neuen C-Standard aufgenommen worden?

Ja. https://stackoverflow.com/a/25672839

@OP, deine Lösung ist korrekt und die portabelste, solltest du es doch 
mal mit nem C++ compiler bauen wollen. Verpacks in eine inline Funktion 
oder ein makro und gut ist.

Ergo70 schrieb:
> Undefined behavior vermutlich

korrekt, UB = undefined behavior. D.h. kann gut gehen, tut es aber nicht 
unter allen Bedingungen. Nicht schön. Auf jeden Fall vermeiden
Gast #7197937
Lesenswert?

jojo schrieb:
> @OP, deine Lösung ist korrekt und die portabelste, solltest du es doch
> mal mit nem C++ compiler bauen wollen. Verpacks in eine inline Funktion
> oder ein makro und gut ist.

Ich bin etwas erstaunt/erschrocken, dass (std::)memcpy noch nicht 
genannt wurde. DAS ist die portable Methode in C++ und auch C, und wird 
ohnehin weboptimiert.

Seppel schrieb:
> Harry L. schrieb:
>> https://www.learn-c.org/de/Unions
>
> Das ist undefigned, wegen dem Alignement, oder? Bei Embedded habe ich
> ewig keine Unions gesehen, ...

Nein, weil das größte alignment der member für den ganzen union zählt. 
Somit gibt es kein Alignmentproblem.
1
// works for C and C++
2

3
#ifdef __STDC__
4
# include <stdint.h>
5
#else
6
# include <cstdint>
7
#endif
8

9
#ifdef __STDC__
10
# include <assert.h>
11
# include <stdalign.h>
12
#endif
13

14
typedef union {
15
    uint8_t i[24];
16
} A;
17

18
typedef union {
19
    uint8_t i[24];
20
    uint64_t j[3];
21
} B;
22

23
static_assert(sizeof(A) == sizeof(B));
24
static_assert(alignof(A) == 1);
25
static_assert(alignof(B) == 8);
Persönliche Seite #7197951
Lesenswert?

Huch schrieb:
> Ich bin etwas erstaunt/erschrocken, dass (std::)memcpy noch nicht
> genannt wurde. DAS ist die portable Methode in C++ und auch C, und wird
> ohnehin weboptimiert.

Es ist zwar legal, aber das Ergebnis ist trotzdem plattformabhängig und 
damit nicht besonders portabel. Was am Ende in "arr" steht hängt von der 
Byte-Reihenfolge ab. Die Original-Lösung mit Bitshifts hingegen ist 
portabel, denn da ist das Ergebnis immer gleich.

Ich hatte das mal hier alles zusammengefasst: Serialisierung
Gast #7198000
Lesenswert?

Niklas G. schrieb:
> Es ist zwar legal, aber das Ergebnis ist trotzdem plattformabhängig und
> damit nicht besonders portabel. Was am Ende in "arr" steht hängt von der
> Byte-Reihenfolge ab. Die Original-Lösung mit Bitshifts hingegen ist
> portabel, denn da ist das Ergebnis immer gleich.
>
> Ich hatte das mal hier alles zusammengefasst: Serialisierung

Ganz meine Meinung!
Die portable Lösung nehmen, den Rest optimiert eh der Compiler. Da merkt 
man keinen Geschwindigkeitsunterschied. Ausser, das Programm macht 
einzig diese Konvertierungen und nichts anderes.
#7198016
Lesenswert?

PittyJ schrieb:
> den Rest optimiert eh der Compiler.

Für amd64 macht der GCC aus dem Codeschnipsel im Eröffnungspost genau 
drei ASM-Anweisungen:
1
        mov     eax, DWORD PTR Tx_PP[rip]
2
        bswap   eax
3
        mov     DWORD PTR arr[rip], eax
für ARM in etwa dasselbe.

interessanter ist, was für 8-Bit-Architekturen dabei rauskommt, oder auf 
BigEndian-Maschinen, oder für eine Schleife über ein größeres Array, die 
sich evtl. per AVX&co weiter optimieren ließe...
Gast #7198036
Lesenswert?

Huch schrieb:
> Ich bin etwas erstaunt/erschrocken, dass (std::)memcpy noch nicht
> genannt wurde.

Wurde 20 Stunden vorher genannt:

A. S. schrieb:
> Und ja, manche nehmen memcpy, unions oder was auch immer

Huch schrieb:
> DAS (memcpy) ist die portable Methode in C++ und auch C,
> und wird ohnehin weboptimiert.

Ich denke nicht, denn memcpy ordnet die Reihenfolge der Bytes ebenso 
wenig um, wie unions.
#7198410
Lesenswert?

MaWin schrieb:
> Wilhelm M. schrieb:
>> - in C ist type-punning per union explizit ok.
>
> Wäre mir neu. Ist das in einem neuen C-Standard aufgenommen worden?

Naja, so arg neu ist das nicht. Es wurde mit C99 aufgenommen, also vor 
23 Jahren.
Ich finde unions dafür aber trotzdem wenig geeignet. Sie sind eigentlich 
dafür nicht gedacht, und ich finde es auch umständlich, extra nur für 
die Umwandlung einen neuen Datentyp definieren zu müssen.
Ja nachdem, ob es einfach oder portabel sein soll, würde ich entweder 
die memcpy-Variante oder die mit den Shifts verwenden.
Gast #7198518
Lesenswert?

Die memcpy Variante funktioniert nur mit den drei großen Compilern, und 
auch nur wenn die Kommandozeilenparameter passen.
UB sind alle Varianten wegen Bitreihenfolge. Das &0xff ist überflüssig, 
wenn man gerne viel schreibt, kann man explizit casten.
Persönliche Seite #7198526
Lesenswert?

udok schrieb:
> Die memcpy Variante funktioniert nur mit den drei großen Compilern, und
> auch nur wenn die Kommandozeilenparameter passen.

Nein, der Standard schreibt vor dass das funktionieren muss. Daher 
können alle Compiler das. Lediglich das Ergebnis ist plattform-abhängig 
(IB).

udok schrieb:
> UB sind alle Varianten wegen Bitreihenfolge.

Wenn schon Bytereihenfolge, und es ist IB, nicht UB.
Gast #7198702
Lesenswert?

Rolf M. schrieb:
> Ja nachdem, ob es einfach oder portabel sein soll, würde ich entweder
> die memcpy-Variante oder die mit den Shifts verwenden.

Ich bin kein SW-Guru, haut mich also nicht 😀.
Ich hätte das mit einem uint8_t-Pointer auf das uint32_t Array versucht. 
Das müsste doch genauso funktionieren wie ein union-Konstrukt?
Natürlich mit den selben Einschränkungen bez. der Byteorder und ggf. der 
Portierbarkeit.

Bezüglich Portierbarkeit: oft hat man genau eine Umgebung, einen 
Compiler und eine Plattform. Dann braucht die Portierbarkeit nicht 
zwingend eine wichtige Randbedingung sein, wenn es in der Umgebung den 
Zweck erfüllt. Ein Kommentar diesbezüglich im Quelltext wäre sicher 
nicht falsch!
Persönliche Seite #7198719
Lesenswert?

HildeK schrieb:
> Ich hätte das mit einem uint8_t-Pointer auf das uint32_t Array versucht.
> Das müsste doch genauso funktionieren wie ein union-Konstrukt?

In C ist es identisch, in C++ ist es sogar besser als die union, denn 
wie schon geschrieben ist die Verwendung von union's auf diese Art in 
C++ fehlerhaft. Im Endeffekt ist es aber exakt das Gleiche wie memcpy(), 
denn das benutzt "nominell" einen char-Pointer.

HildeK schrieb:
> Bezüglich Portierbarkeit: oft hat man genau eine Umgebung, einen
> Compiler und eine Plattform. Dann braucht die Portierbarkeit nicht
> zwingend eine wichtige Randbedingung sein

Das haben sich schon viele gedacht. Dann kam AMD64 um die Ecke. Und dann 
ARM. Und dann ARM64... Gerade wenn man die korrekte Variante mit den 
Bitshifts schon da hat muss man ja nicht auf was weniger portables 
wechseln?
#7198756
Lesenswert?

Εrnst B. schrieb:
> Für amd64 macht der GCC aus dem Codeschnipsel im Eröffnungspost genau
> drei ASM-Anweisungen:

Und das auch nur, weil der Codeschnipsel vom Anfang nämlich implizit 
auch noch eine Konvertierung nach Big-Endian macht. (im uint8_t array 
sind die Daten in Big-Endian abgelegt, und zwar immer, unabhängig von 
der Byteorder der CPU). Sonst wären es sogar nur zwei Anweisungen.

Deswegen sind alle Vorschläge mit memcpy hier sogar komplett falsch...
Gast #7198796
Lesenswert?

Ich würde auf jeden fall die Bitshifts nehmen. Das kann man auch in eine 
Funktion packen. Wird alles wegoptimiert. 
https://godbolt.org/z/MGjYEKzWc

Auf godbolt.org kann man auch verschiedene Compiler ausprobieren. Jeder 
dort kriegt das wegoptimieren hin. Naja, fast, msvc zum Beispiel schafft 
es nicht einmal, den Code zu kompilieren. MS hat es also immernoch nicht 
geschafft, einen funktionierenden C Compiler zu machen...
1
#include <stdint.h>
2
#include <stdio.h>
3

4
static inline void u32_u8_be(uint8_t x[static const restrict 4], uint32_t u){
5
  x[0] = u >> 24;
6
  x[1] = u >> 16;
7
  x[2] = u >>  8;
8
  x[3] = u;
9
}
10

11
int main(){
12
  uint8_t x[5] = {0};
13
  u32_u8_be(x, 0x6d656f77);
14
  puts((char*)x);
15
  return 0;
16
}

Das Ursprüngliche Beispiel des TO war übrigens auch Big Endian, und all 
die anderen hier haben nun host byte order, auf den meisten Rechnern 
little endian, Beispiele gebracht.
Gast #7198925
Lesenswert?

HildeK schrieb:
> Ein Kommentar diesbezüglich im Quelltext wäre sicher
> nicht falsch!

A. S. schrieb:
> manche [...] sichern dass mit ein paar asserts oder Randbedingungen ab.
> Sauber ist das nicht immer, aber oft schneller oder pragmatischer,
> wenn ich z.B. nur einen pointercast brauche.

Es gibt verschiedene Möglichkeiten, Alignment oder Endianess zur 
Compilezeit oder spätestens zur Laufzeit zu prüfen. Das ist m.E. einem 
Kommentar vorzuziehen.
Gast #7199495
Lesenswert?

Michael D. schrieb:
> Wilhelm M. schrieb:
>> HildeK schrieb:
>>> Ein Kommentar diesbezüglich im Quelltext wäre sicher
>>> nicht falsch!
>>
>> Echt jetzt?
>
> Klar doch, dann kann man nachdem man den bug gefunden hat ganz klar
> sehen, welcher Autor das bewusst unportabel programmiert hat und ihm in
> den Hintern treten oder ihm kündigen.

Ich hatte extra darum gebeten, mich nicht zu hauen 😀.
Wenn ich mal Software schreibe, ist die für mich selber. Ich bin kein 
Softwareentwickler und verwende die µCs im Wesentlichen deshalb, weil 
ich damit kleiner Projekte mit minimalem HW-Aufwand flexibel durchführen 
kann. Mich kann auch niemand mehr kündigen. Und meine Plattform und 
Umgebung ist über Jahre gleich geblieben. Da reicht für mich selber 
tatsächlich ein diesbezüglicher Kommentar für den unwahrscheinlichen 
Fall, dass ich das Schnipsel möglicherweise mal wo ganz anders 
wiederverwenden will.
Nur unter den Bedingungen wollte ich das verstanden wissen ...

Niklas G. schrieb:
> Wie gesagt, nicht unbedingt wenn es um lokale Variablen geht. Dann wird
> das gern mal wegoptimiert.

Danke für die Info. Ich hatte das so nicht auf dem Schirm. Sobald aber 
mal ein Array statisch oder global angelegt wird, dürfte das nicht mehr 
der Fall sein.
#7199668
Lesenswert?

HildeK schrieb:
> Rolf M. schrieb:
>> Ja nachdem, ob es einfach oder portabel sein soll, würde ich entweder
>> die memcpy-Variante oder die mit den Shifts verwenden.
>
> Ich bin kein SW-Guru, haut mich also nicht 😀.
> Ich hätte das mit einem uint8_t-Pointer auf das uint32_t Array versucht.

Und dann? Nehme ich diesen Pointer, um per for-Schleife den Inhalt ins 
Zielarray zu kopieren? Welchen Vorteil sollte das gegenüber memcpy 
haben?

> Das müsste doch genauso funktionieren wie ein union-Konstrukt?

Bei der union kopiere ich es sogar zweimal: Einmal von der Quelle in die 
union und einmal von der union ins Zielarray.
Wobei ein vernünftiger Compiler am Ende aus allen Varianten den selben 
oder zumindest gleichwertigen Code generiert.

> Bezüglich Portierbarkeit: oft hat man genau eine Umgebung, einen
> Compiler und eine Plattform. Dann braucht die Portierbarkeit nicht
> zwingend eine wichtige Randbedingung sein, wenn es in der Umgebung den
> Zweck erfüllt.

Meine Erfahrung aus fast 30 Jahren Programmierung: Wenn der Aufwand für 
die portable Lösung nicht signifikant größer ist als für die unportable, 
dann ist es sinnvoll, erstere umzusetzen.
Oft genug wird aber das Thema Portabilität anfangs komplett ignoriert, 
und wenn es dann später doch mal portiert werden soll, hat man einen 
riesigen Aufwand, der erheblich kleiner hätte sein können, ohne vorher 
großartig mehr Aufwand reingesteckt zu haben. Deshalb achte ich immer 
von Anfang an darauf, Dinge höchstens dann unportabel zu machen, wenn 
sie an der Stelle für mich einen signifikanten Vorteil gegenüber der 
portablen Variante haben.

> Ein Kommentar diesbezüglich im Quelltext wäre sicher nicht falsch!

Besser dann ein paar asserts, die fehlschlagen, wenn die getroffenen 
Annahmen über das System nicht zutreffen.
Gast #7199922
Lesenswert?

Rolf M. schrieb:
> Und dann? Nehme ich diesen Pointer, um per for-Schleife den Inhalt ins
> Zielarray zu kopieren? Welchen Vorteil sollte das gegenüber memcpy
> haben?

Nein, nicht kopieren. Ich dachte an den Zugriff auf die einzelnen Bytes 
des uint32_t-Arrays. Keine Kopie erstellen, denn dann natürlich memcpy.
So in der Art:
1
int main(void) {
2
  int i;
3
  uint32_t var32[10] = {11,20,300,4000,50000,125,7463,554321,0x1FFFFFF2,5};
4
  uint8_t *var8;
5
  var8 = (uint8_t*) var32;
6

7
    for (i=0; i<40; i++)
8
    {
9
      printf ("%X  ", var8[i]);
10
    }
11
}
Aber es ist nicht ganz das, was der TO wollte - er wollte ein zweites 
Array.
Gast #7200045
Lesenswert?

@DerEgon und @Ernst B.
Mir ging es nur darum, zu verdeutlichen, was ich ganz oben meinte.
Danke für die Ergänzungen!
Wobei: auf var8[(i&~3)+3-(i&3)] wäre ich nicht gekommen, ich verstehe 
nicht mal, wie es funktioniert, aber es tut 😀.
#7200057
Lesenswert?

Niklas G. schrieb:
> Naja, es kommt die umgekehrte Reihenfolge vom Host raus. Wenn der TE von
> Big Endian ausgeht, kommt Little Endian raus, also genau falsch.

Stimmt natürlich.

HildeK schrieb:
> Wobei: auf var8[(i&~3)+3-(i&3)] wäre ich nicht gekommen, ich verstehe
> nicht mal, wie es funktioniert, aber es tut 😀.

Es verwurschtelt die zwei niedrigsten Bits in der Adresse (& 3 
entspricht & 0b011), damit diese "rückwärts" zählen.
Erzeugt dabei aber vmtl. furchtbar ineffizienten ASM-Code.
Gast #7200086
Lesenswert?

Niklas G. schrieb:
> Naja, es kommt die umgekehrte Reihenfolge vom Host raus. Wenn der TE von
> Big Endian ausgeht, kommt Little Endian raus, also genau falsch.

Richtig. Er hat es vermutlich auf meinen Ausschnitt bezogen und da kommt 
es mit meiner PC-Umgebung dann beginnend mit dem MSByte richtig rum 
heraus.
Plattform abhängig bleibt es, da hilft nur der vom TO schon im 
Eröffnungspost angegebene Variante.

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