uin8_t array to uin32_t

OP #7483600
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
}
Moderator Persönliche Seite #7483603
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++)

{

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

Moderator (Firma: Titel) Persönliche Seite #7483609
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?

Moderator (Firma: Titel) Persönliche Seite #7483643
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...

#7483690
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ß

Moderator (Firma: Titel) Persönliche Seite #7483694
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.

Persönliche Seite #7483703
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.

Moderator (Firma: Titel) Persönliche Seite #7483710
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.

#7483729
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++?

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

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

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

#7484371
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 ;-)

Moderator (Firma: Titel) Persönliche Seite #7484388
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... ;-)

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

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

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