Forum: Mikrocontroller und Digitale Elektronik uin8_t array to uin32_t


von Zo R. (hsch1978)


Lesenswert?

Hallo,

für eine Berechnung sollen von dem uint8_t array immer 4 Werte benutzt 
werden. Danach die nächsten 4 bytes usw. Wie macht man sowas geschickt 
in C?
1
uint8_t data[64] = { 0x11 ,0x11 ,0x44 ,0x0b ,0x51 ,0x01 ,0x3e ,0x6c ,0x86 ,0x0c ,0x00 ,0x14 ,0x8f ,0xc9,0x8b ,0x1e ,0x2b ,0x29 ,0x88 ,0xe6 ,0xcd ,0xfd ,0xfe ,0x67 ,0x78 ,0x7a ,0x33 ,0x22 ,0x7a,0x47 ,0xed ,0x18 ,0xe2 ,0x50 ,0x0b ,0xcf ,0xfd ,0xe9 ,0x8b ,0xf9 ,0x11 ,0x58 ,0xef ,0x97,0x9e ,0x00 ,0x65 ,0xcc ,0xe5 ,0x9d ,0xdf ,0x87 ,0xf0 ,0x37 ,0x61 ,0xe8 ,0xe7 ,0x52 ,0x13,0x9f ,0x99 ,0x6f ,0x33 ,0x55 };
1
for (uint8_t i = 0; i < 16; i++)
2
{
3
        test ^= *(uint32_t*)(data); // ???
4
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

He S. schrieb:
> Wie macht man sowas geschickt in C?

Da drängen sich direkt mehrere Fragen auf:

- Mikrocontroller: Big oder Little Endian?
- Daten im Array:  Big oder Little Endian?

Was ist für Dich "Geschickt in C"? Auf portable oder schmutzige Art und 
Weise?

Wenn man es portabel macht, erübrigt sich zumindest die erste Frage.

> for (uint8_t i = 0; i < 16; i++)
>
> {
>
>         test ^= *(uint32_t*)(data); // ???
>
> }

Deine Laufvariable wird im Array-Zugriff nicht mit einbezogen, Du 
greifst daher immer auf dieselben Daten zu. Außerdem ist Dein Zugriff 
eher "schmutzig" und nicht portabel. Denn er setzt voraus, dass die 
Endianess des Mikrocontrollers und der Daten identisch ist.

: Bearbeitet durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

He S. schrieb:
> für eine Berechnung sollen von dem uint8_t array immer 4 Werte benutzt
> werden.
Zeig mal exemplarisch an diesen ersten 4 Bytes, wie der gewünschte 
zugehörige 32-Bit-Wert aussehen würde:
1
uint8_t data[64] = { 0x11 ,0x22 ,0x33 ,0x44 , ...
Soll da ein 0x11223344 herauskommen oder ein 0x44332211 oder ein 
0x22114433 oder ein 0x33441122 oder sonstwas Anderes?

: Bearbeitet durch Moderator
von Zo R. (hsch1978)


Lesenswert?

Es soll so herauskommen:
1
0x1111440b
2
51013e6c86
3
...
1
void testFunc(uint8_t *ptr_data)
2
3
uint32_t value;
4
5
for (uint8_t i = 0; i < 16; i++)
6
{
7
  value^= *(uint32_t*)(ptr_data);
8
}


[Mod: Formatierung korrigiert]

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

Da drängt sich sofort die Frage auf, warum muß das Array erstmal 
8-bittig sein?

von Zo R. (hsch1978)


Lesenswert?

Weil die Daten so von einem anderen System herauskommen

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Das nennt sich (De)Serialisierung.

von Peter D. (peda)


Lesenswert?

He S. schrieb:
> Es soll so herauskommen:

Geh nochmal zurück in Dein C-Buch. Dieser Code macht überhaupt nichts.
Eine gerade Anzahl von EXORs mit dem gleichen Wert läßt einen Wert 
unverändert.
Und ein Cast eines Pointers zu einen Typ mit anderer Bitbreite knallt 
ganz gewaltig. Ein Cast ist keine Konvertierung.

von Peter D. (peda)


Lesenswert?

Niklas G. schrieb:
> Das nennt sich (De)Serialisierung.

Aber nicht so!

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Niklas G. schrieb:
>> Das nennt sich (De)Serialisierung.
>
> Aber nicht so!

Im verlinkten Artikel ist erläutert, wie es richtig geht. Aber 
sicherlich wird es hier gleich noch 5x erläutert, mindestens 3x davon 
falsch ;-)

von Peter D. (peda)


Lesenswert?

Niklas G. schrieb:
> Im verlinkten Artikel

Da muß man aber erstmal drauf kommen, daß da ein Link versteckt ist.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Dieser Code macht überhaupt nichts.
Für mich sieht das so aus, als ob eine Art Checksum über ein 16x uint_32 
Feld gemacht werden soll. Und das könnte man tatsächlich unabhängig von 
der Byteorder machen, weil es keinen Übertrag in ein anderes Byte gibt. 
Allerdings müsste man dann den Pointer wenigstens mal hochzählen und 
auch value sinnvoll initialisieren.

Peter D. schrieb:
> Da muß man aber erstmal drauf kommen, daß da ein Link versteckt ist.
Bei mir ist der Link farbig abgesetzt und beim Drüberhovern tut sich 
was...

: Bearbeitet durch Moderator
von Zo R. (hsch1978)


Lesenswert?

Ok ich danke euch.

von Michael M. (michael89)


Lesenswert?

Hallo,
es ist eigl. relative einfach wenn man sich einmal mit Bit-shiften 
beschäftigt hat.
Nimm dir ne größere variable(uint16_t , uint32_t , etc). Danach den 8 
Bit Wert und schriebst den an die richtige Position und ODER_Verknüpft 
die beiden.

ODER --> Variable setzen
UND  --> Variable löschen.
1
uint8_t data[64] = { 0x11 ,0x11 ,0x44 ,0x0b ,0x51 ,0x01 ,0x3e ,0x6c ,0x86 ,0x0c ,0x00 ,0x14 ,0x8f ,0xc9,0x8b ,0x1e ,0x2b ,0x29 ,0x88 ,0xe6 ,0xcd ,0xfd ,0xfe ,0x67 ,0x78 ,0x7a ,0x33 ,0x22 ,0x7a,0x47 ,0xed ,0x18 ,0xe2 ,0x50 ,0x0b ,0xcf ,0xfd ,0xe9 ,0x8b ,0xf9 ,0x11 ,0x58 ,0xef ,0x97,0x9e ,0x00 ,0x65 ,0xcc ,0xe5 ,0x9d ,0xdf ,0x87 ,0xf0 ,0x37 ,0x61 ,0xe8 ,0xe7 ,0x52 ,0x13,0x9f ,0x99 ,0x6f ,0x33 ,0x55 };
2
uint32_t new_data[8];
1
for(int i=0 ; i<8 ; i++){
2
  for(int j=0 ; j < 4 ; j++){
3
    new_data[i] |= ( data[i*5+j] << j*8 );
4
  }
5
}

SerialMonitor:
new data:
0xB441111
0x866C3E01
0xC98F1400
0x88292B1E
0x67FEFDCD
0x7A22337A
0x50E218ED
0x8BE9FDCF


Gruß

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Michael M. schrieb:
> SerialMonitor:
> new data:
> 0xB441111
> 0x866C3E01
Auch nur fast gut, denn

He S. schrieb:
> Es soll so herauskommen:
> 0x1111440b
> 51013e6c86

Und spätestend beim zweiten long geht es schief, denn der Wert 0x51 
taucht bei dir irgendwie nicht auf...

Klar, denn das i*5 ist sicher falsch, denn eine ungerade Zahl ist keine 
Zweierpotenz. Und wir haben es hier ausschließlich mit Zweierpotenzen 
(konkret dem Faktor 4) zu tun.

: Bearbeitet durch Moderator
von Michael M. (michael89)


Lesenswert?

oh jo hast recht.. lösch mal den Beitrag davor und diesen. Ich kann das 
irgendwie nicht. bzw. nicht mal bearbeiten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael M. schrieb:
1
> for(int i=0 ; i<8 ; i++){
2
>   for(int j=0 ; j < 4 ; j++){
3
>     new_data[i] |= ( data[i*5+j] << j*8 );
4
>   }
5
> }

Schon falsch.  new_data wird nicht initialisiert, und es fehlt ein Cast 
nach uint32_t, und i muss mit 4 multipliziert werden:
1
for (int i = 0; i < 8; i++)
2
{
3
    new_data[i] = 0;
4
    for (int j = 0; j < 4; j++)
5
    {
6
        new_data[i] |= (uint32_t) data[i*4 + j] << j*8;
7
    }
8
}

Davon ab gabt's das Thema hier schon gefühlt 1000 Mal.

: Bearbeitet durch User
von Christian M. (christian_m280)


Lesenswert?

Lothar M. schrieb:
> Bei mir ist der Link farbig abgesetzt und beim Drüberhovern tut sich
> was...

Früher waren Links doch mal unterstrichen...

Gruss Chregu

von M. K. (sylaina)


Lesenswert?

Christian M. schrieb:
> Früher waren Links doch mal unterstrichen...

Im Grunde ist das eine Browser-Einstellung ;)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Oder umständlich, aber leicht erkennbar:
1
  for(int i=0 ; i<16 ; i++) {
2
     ui32_data[i] = (uint32_t)data[i*4+0]<<24 | \
3
                    (uint32_t)data[i*4+1]<<16 | \
4
                    (uint32_t)data[i*4+2]<<8  | \
5
                    (uint32_t)data[i*4+3];
6
  }

Christian M. schrieb:
> Früher waren Links doch mal unterstrichen...
Wenn ich drüberfahre kommt der Strich. Seis drum.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Lothar M. schrieb:
> Oder umständlich, aber leicht erkennbar:
>
1
>   for(int i=0 ; i<16 ; i++) {
2
>      ui32_data[i] = (uint32_t)data[i*4+0]<<24 | \
3
>                     (uint32_t)data[i*4+1]<<16 | \
4
>                     (uint32_t)data[i*4+2]<<8  | \
5
>                     (uint32_t)data[i*4+3];
6
>   }
7
>

Da man sowas ja öfters gebrauchen kann, könnte man für die Umwandlung 
von eine Funktion einführen.
1
  for(int i=0 ; i<16 ; i++) {
2
     ui32_data[i] = convert(&data[4 * i]);
3
  }

Und datasollte nach Möglichkeit read-only sein, genauso wie ui32_data 
(was in C aber nicht wirklich machbar ist).
Problematisch ist auch die Abhängigkeit zwischen den Array-Größen.
Daher die Frage: ist wirklich C? Oder C++?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Da man sowas ja öfters gebrauchen kann, könnte man für die Umwandlung
> von eine Funktion einführen.

Tja, wenn man meine uSer-Library einbindet welche ich im o.g. Artikel 
verlinkt habe, steht genau so eine Funktion zur Verfügung:
1
uSer::deserialize (data, ui32_data);

von Wilhelm M. (wimalopaan)


Lesenswert?

Niklas G. schrieb:
> Wilhelm M. schrieb:
>> Da man sowas ja öfters gebrauchen kann, könnte man für die Umwandlung
>> von eine Funktion einführen.
>
> Tja, wenn man meine uSer-Library einbindet welche ich im o.g. Artikel
> verlinkt habe, steht genau so eine Funktion zur Verfügung:
>
1
uSer::deserialize (data, ui32_data);

Ah gut: habe ich übersehen ;-) Hätte ich auch drauf verweisen können.

von Ali K. (teddy50)


Lesenswert?

Warum wird hier keine Union vorgeschlagen?
union
{
 uint8_t array[64];
 uint32_t data[16];
}
Den array im Union mit den obigen Daten füllen und mit data[index] 
zugreifen.
Sollte so funktionieren.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Ali K. schrieb:
> Warum wird hier keine Union vorgeschlagen?
Die Implementierung von Unions ist abhängig von der Plattform und von 
Compilerschaltern, weswegen

Frank M. schrieb:
> Big oder Little Endian?

: Bearbeitet durch Moderator
von Thomas Z. (usbman)


Lesenswert?

ich verwende alternativ oft auch mal ein memcpy(), und halte für diese 
Zwecke ein htonl() vor (*)
1
uint32_t value = 0;
2
uint32_t rdata;
3
for (uint8_t i = 0; i < sizeof(data); i+=4)
4
{
5
  rdata = *(uint32_t*) (data[i]);
6
  //memcpy(&rdata,&data[i],sizeof(uint32_t));
7
  if (MSB_FIRST) rdata = htonl(rdata);
8
  value ^= rdata;
9
}
10
//do something with value

(*)
mir ist schon klar dass htonl eigentlich nicht die richtige Bezeichnung 
ist macht aber genau das was man bei LSB<>MSB braucht.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

He S. schrieb:
> test ^= *(uint32_t*)(data); // ???

Das ist einerseits UB, weil es die aliasing-Regeln verletzt, 
andererseits kann es zu Alignment-Problemen kommen, da data nicht 
unbedingt ein für uint32_t geeignetes Alignment hat.

von M. K. (sylaina)


Lesenswert?

Lothar M. schrieb:
> Oder umständlich, aber leicht erkennbar:

So hätte ich es wahrscheinlich auch gelöst, dann weiß man auch in drei 
Monaten noch mal mit dem ersten Blick, was da genau wie gemacht wird 
bzw. gemacht werden soll. ;)

von Εrnst B. (ernst)


Lesenswert?

Für das reine XOR ist es egal, ob ich 32 Bit auf einmal verarbeite oder 
jedes Byte einzeln.

d.H.:
1
uint32_t chksum(uint8_t* data) {
2
uint8_t test[4]={0,0,0,0};
3
for (int i = 0; i < 64; i+=4){
4
  test[0] ^= data[i]; 
5
  test[1] ^= data[i+1]; 
6
  test[2] ^= data[i+2]; 
7
  test[3] ^= data[i+3];
8
}
9
// erst hier wird die gewünschte Byte-Order wichtig:
10
return test[0]|test[1]<<8|test[2]<<16|test[3]<<24;
11
}

Bevor jemand meckert, dass das ineffektiv ist: Erstmal anschauen was der 
Compiler draus macht (-O3 unrollt die ganze Loop und macht am PC ein 
MMX-XOR daraus, lieber -Os testen), und überlegen wie ein 8-Bit-µC wohl 
ein 32-Bit XOR ausführt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Εrnst B. schrieb:
> Für das reine XOR ist es egal, ob ich 32 Bit auf einmal verarbeite oder
> jedes Byte einzeln.
>
> d.H.:
>
1
> uint32_t chksum(uint8_t* data) {
2
> uint8_t test[4]={0,0,0,0};
3
> for (int i = 0; i < 64; i+=4){
4
>   test[0] ^= data[i];
5
>   test[1] ^= data[i+1];
6
>   test[2] ^= data[i+2];
7
>   test[3] ^= data[i+3];
8
> }
9
> // erst hier wird die gewünschte Byte-Order wichtig:
10
> return test[0]|test[1]<<8|test[2]<<16|test[3]<<24;
11
> }
12
>

Besser
1
uint32_t chksum(const uint8_t* const data) {...}

Und das mit der mag. Zahl 64 würde ich mir auch noch mal überlegen ;-)

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Wilhelm M. schrieb:
> Und das mit der mag. Zahl 64 würde ich mir auch noch mal überlegen ;-)

Selbstverständlich. Hab ich nur schnell so hingeschrieben, weil der 
Compiler sonst die ganze Funktion (mit dem data-Array aus dem Original 
Post) zu einem "return Konstante" zusammengefasst hat.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Εrnst B. schrieb:
1
   return test[0]|test[1]<<8|test[2]<<16|test[3]<<24;
Ja, wir sind ja schon im "von links nach rechts" Kulturkreis, aber 
trotzdem würde ich da für etwas Deobfuscation die bei Zahlen übliche 
absteigende Variante wählen:
1
   return   test[3]<<24 | test[2]<<16 | test[1]<<8 | test[0];

EDIT: und natürlich gehen hier alle 3 Shifts shief, weil wegen uint8_t 
immer 0 rauskommt.

Wilhelm M. schrieb:
> das mit der mag. Zahl 64 würde ich mir auch noch mal überlegen ;-)
Hat nicht jede Zahl ihre eigene Magie?
Und solange das eine Zweierpotenz ist, hege ich da keinen Argwohn... ;-)

: Bearbeitet durch Moderator
von Εrnst B. (ernst)


Lesenswert?

Ok, ich merk schon, mein Beispiel ist arg schiefgegangen... Hätte das 
nicht am x86-64 Compiler testen sollen...

Was ich zeigen wollte:
Byteweise:
1
        mov r30,r24
2
        mov r31,r25
3
        mov r18,r30
4
        mov r19,r31
5
        subi r18,-64
6
        sbci r19,-1
7
        ldi r25,0
8
        ldi r24,0
9
        ldi r23,0
10
        ldi r22,0
11
.L2:
12
        ld r20,Z
13
        eor r22,r20
14
        ldd r20,Z+1
15
        eor r23,r20
16
        ldd r20,Z+2
17
        eor r24,r20
18
        ldd r20,Z+3
19
        eor r25,r20
20
        adiw r30,4
21
        cp r18,r30
22
        cpc r19,r31
23
        brne .L2
24
        ret

vs in der Schleife 32-Bit-Weise:
1
        push r16
2
        push r17
3
        push r28
4
        push r29
5
        rcall .
6
        rcall .
7
        in r28,__SP_L__
8
        in r29,__SP_H__
9
        mov r30,r24
10
        mov r31,r25
11
        mov r24,r30
12
        mov r25,r31
13
        subi r24,-64
14
        sbci r25,-1
15
        std Y+1,__zero_reg__
16
        std Y+2,__zero_reg__
17
        std Y+3,__zero_reg__
18
        std Y+4,__zero_reg__
19
.L2:
20
        ld r20,Z+
21
        ld r21,Z+
22
        ld r22,Z+
23
        ld r23,Z+
24
        ldd r16,Y+1
25
        ldd r17,Y+2
26
        ldd r18,Y+3
27
        ldd r19,Y+4
28
        eor r16,r20
29
        eor r17,r21
30
        eor r18,r22
31
        eor r19,r23
32
        std Y+1,r16
33
        std Y+2,r17
34
        std Y+3,r18
35
        std Y+4,r19
36
        cp r30,r24
37
        cpc r31,r25
38
        brne .L2
39
        mov r25,r19
40
        mov r24,r18
41
        mov r23,r17
42
        mov r22,r16
43
        pop __tmp_reg__
44
        pop __tmp_reg__
45
        pop __tmp_reg__
46
        pop __tmp_reg__
47
        pop r29
48
        pop r28
49
        pop r17
50
        pop r16
51
        ret

bei ansonsten gleichen Compiler-Einstellungen.

von Εrnst B. (ernst)


Lesenswert?

Slightly Offtopic:

Impressive, was der GCC da für AMD64 draus macht:
1
        movdqu  xmm0, XMMWORD PTR [rdi+48]
2
        movdqu  xmm3, XMMWORD PTR [rdi+32]
3
        movdqu  xmm1, XMMWORD PTR [rdi]
4
        movdqu  xmm2, XMMWORD PTR [rdi+16]
5
        pxor    xmm0, xmm3
6
        pxor    xmm1, xmm2
7
        pxor    xmm0, xmm1
8
        movdqa  xmm1, xmm0
9
        psrldq  xmm1, 8
10
        pxor    xmm0, xmm1
11
        movdqa  xmm1, xmm0
12
        psrldq  xmm1, 4
13
        pxor    xmm0, xmm1
14
        movd    eax, xmm0
15
        ret
Läd erst das ganze Input-Array in Register, und XORed das dann Baumartig 
zusammen, bis nur noch ein Register übrig ist, und XORed den Rest dann 
(einmal um 8 bytes, einmal um 4 Bytes verschoben) mit sich selber 
zusammen, und ist fertig.

: Bearbeitet durch User
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.