Ein Char ist 1 Byte und eine Ganzzahl ist 4 Byte. Ich möchte Byte für Byte von einem char [4] in eine ganze Zahl kopieren. Ich habe an verschiedene Methoden gedacht, aber ich bekomme unterschiedliche Antworten.
%Vor%Ausgabe ist 6513249 1633837824 6513249
Welcher ist richtig? Was läuft falsch?
Es ist ein Problem endianness . Wenn Sie char*
als int*
interpretieren, wird das erste Byte der Zeichenkette das niedrigstwertige Byte der Ganzzahl (weil Sie diesen Code auf x86 ausgeführt haben, das ein Little Endian ist), während bei der manuellen Konvertierung das erste Byte zu höchst bedeutsam.
Um dies in Bilder einzufügen, ist dies das Quell-Array:
%Vor% Wenn diese Bytes als Integer in einer Little-Endian-Architektur interpretiert werden, lautet das Ergebnis 0x00636261
, was dezimal 6513249 ist. Andererseits führt die manuelle Platzierung jedes Bytes zu 0x61626300
- dezimal 1633837824.
Natürlich ist das Behandeln von char*
als int*
ein nicht definiertes Verhalten, daher ist der Unterschied in der Praxis nicht wichtig, da Sie die erste Konvertierung nicht wirklich verwenden dürfen. Es gibt jedoch eine Möglichkeit, das gleiche Ergebnis zu erzielen, das als punning type :
Keine der ersten beiden ist richtig.
Die erste Version verletzt die Aliasing-Regeln und kann fehlschlagen, weil die Adresse von str
für unsigned int
nicht richtig ausgerichtet ist. Um die Bytes einer Zeichenkette als unsigned int
mit der Host-System-Byte-Reihenfolge neu zu interpretieren, können Sie sie mit memcpy
:
(Vorausgesetzt, die Größe eines unsigned int
und die Größe von str
sind identisch.)
Der zweite kann mit einem Ganzzahlüberlauf fehlschlagen, weil str[0]
zu einem int
hochgestuft wird, also str[0]<<24
hat den Typ int
, aber der von der Verschiebung benötigte Wert kann größer sein als in int
darstellbar ist . Um dies zu beheben, verwenden Sie:
Diese zweite Methode interpretiert die Bytes von str
in Big-Endian-Reihenfolge, unabhängig von der Reihenfolge der Bytes in einem unsigned int
im Host-System.
Sie haben gesagt, dass Sie Byte für Byte kopieren möchten.
Das bedeutet, dass die Zeile unsigned int a = *(unsigned int*)str;
nicht erlaubt ist. Was Sie jedoch tun, ist eine ziemlich übliche Art, ein Array als einen anderen Typ zu lesen (zB wenn Sie einen Stream von der Festplatte lesen.
Es braucht nur ein paar Feinabstimmungen:
%Vor%Bedenken Sie, dass die Daten, die Sie gerade lesen, nicht die gleiche Endianz haben wie die Maschine, von der Sie lesen. Dies könnte helfen:
%Vor%Beide sind in gewisser Weise richtig:
Ihre erste Lösung kopiert in nativer Byte-Reihenfolge (d. h. die Byte-Reihenfolge, die die CPU verwendet) und kann daher je nach CPU-Typ unterschiedliche Ergebnisse liefern.
Ihre zweite Lösung kopiert in Big-Endian-Byte-Reihenfolge (d. h. das höchstwertige Byte an der niedrigsten Adresse), egal was die CPU verwendet. Es wird den gleichen Wert auf allen Arten von CPUs ergeben.
Was richtig ist, hängt davon ab, wie die ursprünglichen Daten (Array von Zeichen) interpretiert werden sollen.
Z.B. Java-Code (Klassendateien) verwendet immer Big-Endian-Byte-Reihenfolge (unabhängig davon, was die CPU verwendet). Wenn Sie int
s aus einer Java-Klassendatei lesen wollen, müssen Sie den zweiten Weg verwenden. In anderen Fällen möchten Sie vielleicht den CPU-abhängigen Weg verwenden (ich denke, Matlab schreibt int
s in nativer Byte-Reihenfolge in Dateien, vgl. diese Frage ).
Wenn Sie CVI (National Instruments) Compiler verwenden, können Sie die Funktion Scan dazu verwenden:
unsigned int a;
Für großen Endian: Scannen (str, "% 1i [b4uzi1o3210] & gt;% i", & amp; a);
Für kleine Endianer: Scannen (str, "% 1i [b4uzi1o0123] & gt;% i", & amp; a);
Der o-Modifikator gibt die Byte-Reihenfolge an. i innerhalb der eckigen Klammern zeigt an, wo im str-Array zu beginnen ist.
Tags und Links c