uin8_t array to uin32_t

OP #7483600

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

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

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?

OP #7483617

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]

#7483629

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.

Moderator (Firma: Titel) Persönliche Seite #7483643

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

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

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

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.

#7483729

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

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

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

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

Ε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

Ε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

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

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

Eine eigene Frage? Dieser Thread ist über 3 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.