Endianess

 

Come tutti sapete, i dati vengono conservati in memoria. Ogni calcolatore però ha un suo modo di salvarli nel momento in cui il dato da salvare è maggiore di un byte (ad esempio, un valore int che è rappresentato da 4 byte).
Nel caso di più byte da memorizzare, un computer può utilizzare principalmente 2 metodi, inserire in memoria prima il byte meno significativo e poi quello più significativo oppure il contrario. Ecco uno schema per farvi capire meglio

Questo concetto si applica al singolo dato.
Prendiamo, ad esempio, una stringa. Essa è un Array di char. un char è memorizzato in 1 byte (supponiamo che sia codificato col codice ASCII).
Una stringa sarà quindi una successione di char, terminanti con carattere ''. In termini di memoria sarà una successione di 1 byte fino al byte contenente ''.

Prendiamo ora un array di int. Un int è memorizzato in 4 byte. Quindi un array di 4 int sarà memorizzato come sequenza di 4 gruppi da 4 byte.

Il concetto delle Endian vale per il singolo elemento. Ovvero il singolo char o il singolo int, non vale per tutto l'insieme del dato.
La stringa verrà memorizzata un char alla volta, il primo carattere al primo posto, il secondo al secondo ecc...

L' Array di int, invece? Esattamente lo stesso.

Il primo int al primo posto, il secondo al secondo ecc.. Il problema delle endian sta nel come è memorizzato il singolo int che ha bisogno di 4 byte per essere memorizzato. Infatti le endian entrano in gioco ora. Se è memorizzato "in ordine" verranno usate le BIG ENDIAN, in caso contrario le LITTLE ENDIAN.

N.B. Nel caso di una codifica multi Byte (UTF-8 ad esempio), vale quanto appena detto trattandosi di singoli elementi codificati da più byte.

Vediamo ora dei piccoli esempi su come possiamo scoprire che endian utilizzi il nostro computer.

#include <stdio.h>

 

 

// Volendo si possono omettere quete macro

#define LITTLE_ENDIAN 0

#define BIG_ENDIAN    1

 

//VERSIONE MACRO

const int i = 1; //0000000 00000000 00000000 00000001

#define ENDIAN() ( (*(char*)&i) == 0 )

 

//VERSIONE FUNZIONE

int endian()

{

        int   i = 1; //0000000 00000000 00000000 00000001

        char* p = (char *)&i; // 00000000 or 00000001

 

        if(p[0] == 1)

               return LITTLE_ENDIAN;

        else

               return BIG_ENDIAN;

}

 

int main()

{      

        if ( ENDIAN() ) //

               printf("BIG ENDIANn");

        else

               printf("LITTLE ENDIANn");

 

        if ( endian() )

               printf("BIG ENDIANn");

        else

               printf("LITTLE ENDIANn");

 

        getchar();

        return 0;

}

Perché è importante tutto questo? Il problema potrebbe sorgere tra macchine che usano sistemi diversi. Se ogni int viene scritto in un verso e letto nell'altro, non si riuscirebbe più a risalire al suo esatto valore. Perché quelle che per un sistema è il byte meno significativo per l'altro è quello più significativo, in questo modo si invertirebbero i singoli byte del dato.
Esempio:

75

Come si vede, se invia un intero codificato in un modo, e si tentasse di leggere con un'altra Endian (scambiano tipo di endian, il byte 0  resterebbe il Byte 0, il Byte 1 resterebbe il Byte 1 e così via)
Alcuni potrebbero pensare che basti specificare oltre il dato, anche l'endian utilizzata. Ma non è così semplice. In ogni comunicazione tra PC (o in generale tra 2 dispositivi) non sono solo i valori interi ad essere scambiati. Possono esserci in mezzo, stringe ad esempio (che non devono essere invertite),  bit o byte per inizio e fine trasmissione o di controllo della correttezza dei dati ecc. ecc..
Non essendoci degli standard o non essendo nessuna delle due tipologie prevalente sull'altra (in genere entra in gioco pure l'architettura interna dei bus per il trasporto dei dati, ad esempio Intel e Digital usano il formato little endian mentre Motorola, IBM e Sun usano il formato big endian, inoltre il  formato dei dati contenuti nei protocolli di rete è big endian, il bus PCI è little endian, quello del bus VME è big endian ecc.; quello che resta da fare è di specificare per ogni singolo dato l'endian utilizzata, e in caso convertirlo.