Endianness

Ο όρος endianness χρησιμοποιείται στην επιστήμη των υπολογιστών για τον τρόπο αποθήκευσης των σειρών bytes μέσα στην μνήμη του ηλεκτρονικού υπολογιστή. Υπάρχουν δύο τρόποι αναπαράστασης των bytes που αναπαριστάται ένας αριθμός μέσα στην εσωτερική μνήμη του υπολογιστή: η αναπαράσταση big-endian και η αναπαράσταση little-endian. Οι υπολογιστές με επεξεργαστές 80x86 από την Intel όπως και κλώνοι αυτών χρησιμοποιούν αναπαράσταση little-endian ενώ υπολογιστές όπως οι Sun SPARC, Motorola 68K ή η οικογένεια των PowerPC χρησιμοποιούν αναπαράσταση big-endian. Τα πρωτόκολλα δικτύου υπολογιστών κάνουν τους κατάλληλους μετασχηματισμούς δεδομένων ώστε δύο υπολογιστές με διαφορετικό endianness να επικοινωνούν.

Ορολογία

Η ορολογία Endianness, big-endian ή little-endian προέρχεται από το λογοτεχνικό βιβλίο Τα ταξίδια του Γκιούλιβερ. Μέσα στο βιβλίο περιγράφεται η διαμάχη δύο μυθικών λαών για το πως μπορεί κάποιος να σπάσει ένα βρασμένο αυγό, είτε κτυπώντας το στην μυτερή άκρη είτε στην μέση (δηλαδή μεγάλη/big ή μικρή/little άκρη/endianness του αυγού). Στην διαμάχη αυτή (μέσα στο λογοτεχνικό έργο), όσοι υποστήριζαν την μία ή την άλλη τεχνική χαρακτηρίστηκαν ως "big-endians" ή "little-endians" και από από εκεί η ορολογία υιοθετήθηκε στην πληροφορική.

TCP/IP

Πρόβλημα με την αναπαράσταση big-endian ή little-endian δημιουργείται όταν ένας υπολογιστής επικοινωνεί με ένα άλλο υπολογιστή μέσω ενός δικτύου και χρησιμοποιεί διαφορετικό endianness. Για να αποφεύγονται τα προβλήματα αυτά στην επικοινωνίες μέσω δικτύου χρησιμοποιούνται τα πρωτόκολλα επικοινωνίας και γίνονται οι απαραίτητες μετατροπές στην κωδικοποίηση ώστε να επιτευχθεί η δικτυακή μεταφορά δεδομένων.
Στο μοντέλο TCP/IP και σε όλα τα επίπεδα επικοινωνίας χρησιμοποιείται η κωδικοποίηση "big-endian". Οποιαδήποτε τιμή 16 ή 32bit μεταφέρεται μεταξύ των επιπέδων του TCP/IP όπως η IP διεύθυνση ή αριθμητικά ελέγχου (checksums) κλπ θα πρέπει να έχουν το σημαντικότερο byte πρώτο (όπως καθορίζεται με την κωδικοποίηση "big-endian"). Η big-endian κωδικοποίηση αυτή ονομάζεται και "network byte order". Υπάρχουν οι συναρτήσεις htonl, htons, ntohl, ntohs στην γλώσσα προγραμματισμού C οι οποίες χρησιμοποιούνται για το μετασχηματισμό μεταξύ των δύο κωδικοποιήσεων endianness για την επίτευξη επικοινωνίας υπολογιστών με διαφορετικό endianness.

Παράδειγμα προβλήματος endianness στην C

Το παρακάτω κομμάτι κώδικα μεταγλωττίζεται χωρίς λάθη και σε υπολογιστές όπου χρησιμοποιείται little-endian και σε υπολογιστές όπου χρησιμοποιείται big-endian κωδικοποίηση. Το πρόγραμμα αυτό αρχικοποιεί μια δομή με τρεις μεταβλητές (δύο συμβολοσειρές/πίνακες χαρακτήρων και ενός ακεραίου αριθμού) και στην συνέχεια την γράφει στο εξωτερικό αρχείο data.one.
#include <stdio.h>#include <string.h>int main (int argc, char* argv[]) {
    FILE* fp;

    // Παράδειγμα μιας δομής δεδομένων 
    struct {
        char one[4];
        int  two;
        char three[4];
    } data;

    // Αρχικοποίηση της δομής data με δεδομένα 
    strcpy (data.one, "foo");
    // Δεκαεξαδικός αριθμός 01234567 
    data.two = 0x01234567;
    strcpy (data.three, "bar");

    // Εγγραφή των δεδομένων σε ένα εξωτερικό αρχείο 
    fp = fopen ("output", "wb");
    if (fp) {
        fwrite (&data, sizeof (data), 1, fp);
        fclose (fp);
    }}
Οι συμβολοσειρές/αλφαριθμητικά (strings) "foo" και "bar" θα αποθηκευτούν με τον ίδιο τρόπο και σε μηχανήματα όπου χρησιμοποιείται little-endian αλλά και σε υπολογιστές όπου χρησιμοποιείται big-endian. Ο δεκαεξαδικός αριθμός (01234567)₁₆ (δηλαδή ο 0x01234567) θα γραφτεί με διαφορετική κωδικοποίηση σε υπολογιστή little-endian από ότι σε υπολογιστή big-endian. Αυτό αποτελεί πρόβλημα, αν αργότερα το αρχείο αυτό διαβαστεί σε υπολογιστή με διαφορετική κωδικοποίηση endianness.

Έλεγχος endianness στην C

Ο παρακάτω κώδικας ελέγχει την κωδικοποίηση endianness του υπολογιστή και μας δείχνει πως κωδικοποιείται ο δεκαεξαδικός αριθμός (01234567)₁₆ μέσα στις θέσεις μνήμης του υπολογιστή.
#include <stdio.h>#define LITTLE_ENDIAN 0#define BIG_ENDIAN    1void show_bytes (char *start, int len) {
        int i;

        for (i=0;i<len;i++)
                // %p Εκτυπώνει τον δείκτη \t Tab-κενό %x Τύπωμα τιμών σε δεκαεξαδικό \n νέα γραμμή
                printf("%p\t0x%.2x\n",start+i, *(start+i));
        printf("\n");}void show_int (int x) {
        show_bytes( (char *) &x, sizeof(int));}int endianness() {
    int i = 1;
    // παίρνει την διεύθυνση του i και την κάνει cast
    // στον δείκτη char *p.
    char *p = (char *)&i;

    if (p[0] == 1)
        return LITTLE_ENDIAN;
    else
        return BIG_ENDIAN;}int main (int argc, char* argv[]) {

   int i;

   printf("Ο υπολογιστής χρησιμοποιεί την κωδικοποίηση: ");
   if (endianness()==BIG_ENDIAN)
      printf("big-endian \n");
   else
      printf("little-endian \n");

   i=0x01234567;
   printf("ο αριθμός int i=0x01234567 (δηλαδή ο %i στο δεκαδικό) έχει αποθηκευτεί ως:\n",i);
   show_int(i);
   return 0;}
Παρακάτω φαίνονται οι θέσεις μνήμης (σε κάθε εκτέλεση του προγράμματος εμφανίζονται διαφορετικές θέσεις) και οι τιμές που αποθηκεύτηκαν. Το λιγότερο σημαντικό ψηφίο του αριθμού (01234567)₁₆ το (67)₁₆ αποθηκεύτηκε στην "μικρότερη" θέση μνήμης, εδώ στην διεύθυνση 0xbfab6ff0 ή (bfab6ff0)₁₆ κάτι το οποίο δείχνει κωδικοποίηση "little-endian".

Ο υπολογιστής χρησιμοποιεί την κωδικοποίηση: little-endian
ο αριθμός int i=0x01234567 (δηλαδή ο 19088743 στο δεκαδικό) έχει αποθηκευτεί ως:
0xbfab6ff0      0x67
0xbfab6ff1      0x45
0xbfab6ff2      0x23
0xbfab6ff3      0x01