2012-07-10 15 views
11

Ich lief das folgende Programm auf Little-Endian [LE] -Maschine [Linux, Intel-Prozessor]. Ich bin nicht in der Lage, die 3 Ausgänge im untenstehenden Codeschnipsel zu erklären. Da die Maschine LE ist, wird der Wert a als 0x78563412 gespeichert. Beim Drucken wird der tatsächliche Wert angezeigt. Da es ein LE-Gerät ist, erwarte ich ntohl() zu einem No-Op- und Display 0x78563412, was es tut. Ich erwarte jedoch 0x12345678 für die zweite Druckanweisung, die htonl() enthält. Kann mir bitte jemand helfen zu verstehen, warum sie gleich sind?Gleiche Ausgabe für Htonl() und Ntohl() auf einer ganzen Zahl

int main() 
{ 
    int a = 0x12345678; 

    printf("Original - 0x%x\n", (a)); 
    printf("Network - 0x%x\n", htonl(a)); 
    printf("Host - 0x%x\n", ntohl(a)); 

    return 0; 
} 

Ausgang:

Original - 0x12345678 
Network - 0x78563412 
Host - 0x78563412 

Antwort

23

Seit seiner einer LE Maschine zu vorbei, erwarte ich ntohl() einen No-op

zu sein, dass der Fehler ist. Netzwerk-Byte-Reihenfolge ist Big-Endian, Host-Byte-Reihenfolge ist Little-Endian. Daher geben sowohl ntohl als auch htonl eine Byte-vertauschte Version ihrer Eingabe zurück.

Denken Sie daran, der Punkt der htonl ist, dass Sie eine ganze Zahl auf dem Host zu nehmen, dann schreiben:

int i = htonl(a); 

und das Ergebnis ist, dass die Erinnerung an i, wenn mit Netzwerk-Byte-Reihenfolge interpretiert, hat den gleichen Wert, der a tut. Wenn Sie also die Objektdarstellung von i in einen Socket schreiben und der Leser am anderen Ende eine 4-Byte-Ganzzahl in der Netzwerkbyte-Reihenfolge erwartet, wird der Wert a gelesen.

und Anzeige 0x78563412

Ist das, was Sie schreiben wollte? Wenn ntohl eine No-Op-Funktion (oder vielmehr eine Identitätsfunktion) wäre, würde Ihre dritte Zeile das gleiche wie Ihre erste Zeile ausgeben, weil Sie ntohl(a) == a hätten. Dies ist, was auf Big-Endian-Implementierungen geschieht, wo Ihr Programm druckt:

Original - 0x12345678 
Network - 0x12345678 
Host - 0x12345678 
+0

Als @Alok unten erwähnt, erwartete ich, dass das folgende Verhalten immer wahr sein wird: 'x == htonl (ntohl (x))'. Aber das passiert nicht und Ihre Erklärung war sehr hilfreich. – Bhaskar

+2

@Bhaskar: Brian Roachs Punkt ist auch wichtig, dann: du hast nie 'htonl (ntohl (a))' berechnet. Sie haben 'htonl (a)' und 'ntohl (a)' berechnet. –

+0

vielen Dank! Große Erklärung – Skully

5

Weil Sie a von Wert sind vorbei und daher ist es nicht durch eine dieser beiden Funktionen verändert.

Sie drucken, was htonl() und ntohl() sind zurück.

Bearbeiten zu hinzufügen: Ich vermisste, wo Sie dachten, man wäre ein No-Op. Es ist nicht. Beide werden genau dasselbe auf einer LE-Maschine tun; Umkehr der Byte-Reihenfolge. ntohl() erwartet Sie ein Netzwerk-Byte bestellt int

9

htonl und ntohl sind genau die gleichen Funktionen. Sie sollen htonl(ntohl(x)) == x erfüllen. Sie werden nur für die Dokumentation anders benannt (Sie machen explizit, dass Sie von Host zu Netzwerk oder umgekehrt konvertieren, auch wenn es das gleiche ist).Auf einer Little-Endian-Maschine führen beide also Byte-Swapping durch, und auf einer Big-Endian-Maschine sind beide No-Ops.

+5

Auf einer hypothetischen "stupid-endian" C-Implementierung, wo die Bytes weder Big-Endian (geordnete '4321') noch Little-Endian (geordnete' 1234'), sondern zum Beispiel '3214' angeordnet sind, hätten Sie immer noch 'htonl (ntohl (x))', aber 'htonl' und' ntohl' würden nicht dasselbe tun, sie wären 8-Bit-Rotationen in entgegengesetzte Richtungen. Ich hoffe, dass es keine solche Architektur gibt, aber es könnte die Sockets-API implementieren, dank der Tatsache, dass 'htonl' und' ntohl' separate Funktionen sind. –

+0

@SteveJessop du hast natürlich Recht. Das ist wahrscheinlich der Grund, warum wir zwei verschiedene Funktionen haben. http://en.wikipedia.org/wiki/Endianness#Middle-endian –

+0

@SteveJessop Ja, ich habe das gerade herausgefunden und versucht meinen Kommentar zu löschen, aber du bist schnell! :-). –

0

In Ihrem Programm, wenn Sie schreiben int a;Sie wissen, dass a enthält eine Host-Ganzzahl bestellt, wird das Programm nicht wissen. Sie könnten genauso einfach einen int angeben, der bereits einen Wert in der Netzwerkreihenfolge enthält. Wenn Sie einen arithmetischen Operator für einen Wert verwenden, der sich nicht in der Host-Reihenfolge befindet, ist das Ergebnis natürlich aus Sicht des Netzwerks falsch, wenn die Netzwerkreihenfolge nicht der Host-Reihenfolge entspricht.

Aber das ist nicht so weit hergeholt, die netzwerksortierten Werte werden oft genau so in Strukturen niedriger Ebene gehalten, bevor sie gesendet werden oder kurz nachdem sie empfangen wurden.

Was mit Ihrem Programm falsch ist, ist, dass, wenn Sie ntohl() rufen Sie sind viel versprechend in die ntohl() Funktion, die die int Sie bieten einen gewissen Wert im Speicher in Netzwerk Reihenfolge gespeichert ist. Das ist der Vertrag. Wenn es nicht wahr ist, wird die Funktion nicht das leisten, was Sie erwarten, und das ist es, was Sie sehen.

Wie andere auf den meisten Systemen (große oder kleine, aber nicht dumm-Endian) erklärt, sind die beiden Funktionen normalerweise identisch, entweder eine umgekehrte Byte oder ein No-Op.