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?
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.
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_tdata[64]={0x11,0x22,0x33,0x44,...
Soll da ein 0x11223344 herauskommen oder ein 0x44332211 oder ein
0x22114433 oder ein 0x33441122 oder sonstwas Anderes?
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.
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 ;-)
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...
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.
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.
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
Da man sowas ja öfters gebrauchen kann, könnte man für die Umwandlung
von eine Funktion einführen.
1
for(inti=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++?
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:
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.
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.
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?
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.
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. ;)
Für das reine XOR ist es egal, ob ich 32 Bit auf einmal verarbeite oder
jedes Byte einzeln.
d.H.:
1
uint32_tchksum(uint8_t*data){
2
uint8_ttest[4]={0,0,0,0};
3
for(inti=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
returntest[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.
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.
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
returntest[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... ;-)
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.