I processori Intel (e forse alcuni altri) usano il formato little endian per l'archiviazione.
Mi chiedo sempre perché qualcuno vorrebbe memorizzare i byte in ordine inverso. Questo formato ha qualche vantaggio rispetto al formato big endian?
Ci sono argomenti in entrambi i casi, ma un punto è che in un sistema little-endian, l'indirizzo di un dato valore in memoria, preso come una larghezza di 32, 16 o 8 bit, è lo stesso.
In altre parole, se hai in memoria un valore di due byte:
0x00f0 16
0x00f1 0
prendendo quel '16' come valore a 16 bit (c 'breve' sulla maggior parte dei sistemi a 32 bit) o come valore a 8 bit (generalmente c 'char') cambia solo l'istruzione di recupero che usi - non il indirizzo da cui recuperare.
Su un sistema big-endian, con quanto sopra esposto come:
0x00f0 0
0x00f1 16
avresti bisogno di incrementare il puntatore e quindi eseguire l'operazione di recupero più stretta sul nuovo valore.
Quindi, in breve, "su sistemi little endian, i cast sono un no-op."
I always wonder why someone would want to store the bytes in reverse order.
Big-endian e little-endian sono solo "ordine normale" e "ordine inverso" da una prospettiva umana, e solo se tutti questi sono veri ...
Queste sono tutte convenzioni umane che non hanno alcuna importanza per una CPU. Se dovessi conservare il n. 1 e il n. 2 e girare n. 3, little-endian sembrerebbe "perfettamente naturale" per le persone che leggono l'arabo o l'ebraico, che sono scritte da destra a sinistra.
E ci sono altre convenzioni umane che rendono big-endian che sembrano innaturali, come ...
Ai tempi in cui programmavo principalmente 68K e PowerPC, ritenevo che big-endian fosse "giusto" e little-endian fosse "sbagliato". Ma da quando lavoro su ARM e Intel, mi sono abituato a little-endian. Non importa davvero.
OK, ecco la ragione perché mi è stato spiegato: Addizione e sottrazione
Quando aggiungi o sottragga numeri a più byte, devi iniziare con il byte meno significativo. Se si aggiungono due numeri a 16 bit, ad esempio, potrebbe esserci un carry dal byte meno significativo al byte più significativo, quindi è necessario iniziare con il byte meno significativo per vedere se c'è un carry. Questa è la stessa ragione per cui si inizia con la cifra più a destra quando si esegue l'aggiunta a mano lunga. Non puoi iniziare da sinistra.
Considera un sistema a 8 bit che recupera i byte sequenzialmente dalla memoria. Se recupera il byte meno significativo prima , può iniziare a fare l'aggiunta mentre il byte più significativo viene prelevato dalla memoria. Questo parallelismo è il motivo per cui le prestazioni sono migliori in little endian su sistemi come. Se dovesse attendere fino a quando entrambi i byte sono stati recuperati dalla memoria, o recuperarli nell'ordine inverso, ci sarebbe voluto più tempo.
Questo è su vecchi sistemi a 8 bit. Su una CPU moderna dubito che l'ordine dei byte fa alcuna differenza e usiamo little endian solo per ragioni storiche.
Con processori a 8 bit era sicuramente più efficiente, si poteva eseguire un'operazione a 8 o 16 bit senza bisogno di codice diverso e senza bisogno di bufferizzare valori aggiuntivi.
È ancora meglio per alcune operazioni di aggiunta se stai trattando un byte alla volta.
Ma non c'è ragione per cui big-endian sia più naturale - in inglese si usa tredici (little endian) e ventitre (big endian)
La convenzione di data giapponese è "big endian" - aaaa / mm / gg. Questo è utile per l'ordinamento degli algoritmi, che può utilizzare una semplice stringa: confronta con la solita regola del primo carattere è la più significativa.
Qualcosa di simile si applica ai numeri big-endian memorizzati in un record di campo più significativo. L'ordine di importanza dei byte all'interno dei campi corrisponde alla significatività dei campi all'interno del record, quindi è possibile utilizzare un memcmp
per confrontare i record, senza preoccuparsi molto se si confrontano due parole chiave, quattro parole o otto byte separati.
Capovolgi l'ordine di importanza dei campi e ottieni lo stesso vantaggio, ma per i numeri little-endian piuttosto che big-endian.
Questo ha pochissimo significato pratico, ovviamente. Se la tua piattaforma è big-endian o little-endian, puoi ordinare un campo record per sfruttare questo trucco se davvero ne hai bisogno. È solo un problema se devi scrivere codice portatile .
Potrei anche includere un link al classico appello ...
Modifica
Un pensiero in più. Una volta ho scritto una grande libreria di interi (per vedere se potevo), e per questo, i blocchi di 32 bit sono memorizzati in ordine little-endian, indipendentemente da come la piattaforma ordina i bit in quei blocchi. Le ragioni erano ...
Molti algoritmi iniziano naturalmente a funzionare alla fine meno significativa, e vogliono che queste estremità siano abbinate. Ad esempio, i port sono propogati a cifre sempre più significative, quindi ha senso iniziare alla fine meno significativa.
Aumentare o ridurre un valore significa solo aggiungere / rimuovere blocchi alla fine - non è necessario spostare i blocchi su / giù. La copia potrebbe essere ancora necessaria a causa della riallocazione della memoria, ma non di frequente.
Ovviamente questo non ha alcuna rilevanza evidente per i processori - fino a quando le CPU non sono supportate da hardware con supporto di interi, è puramente una cosa da libreria.
Nessun altro ha risposto PERCHÉ questo potrebbe essere fatto, molte cose sulle conseguenze.
Considera un processore a 8 bit che può caricare un singolo byte dalla memoria in un dato ciclo di clock.
Ora, se vuoi caricare un valore di 16 bit, in (diciamo) l'unico registro a 16 bit che hai - cioè il contatore del programma, allora un modo semplice per farlo è:
il risultato: si incrementa sempre la posizione del recupero, si carica sempre solo nella parte di ordine basso del registro più ampio e si deve solo essere in grado di spostarsi a sinistra. (Ovviamente, lo spostamento a destra è utile per altre operazioni, quindi questo è un po 'un side show.)
Una conseguenza di ciò è che il materiale a 16 bit (doppio byte) è archiviato in ordine di Most .. Least. Ad esempio, l'indirizzo più piccolo ha il byte più significativo, quindi big endian.
Se invece hai provato a caricare usando little endian, dovresti caricare un byte nella parte inferiore del tuo registro wide, quindi caricare il byte successivo in un'area di staging, spostarlo e quindi inserirlo nella parte superiore di il tuo registro più ampio. Oppure usa una disposizione più complessa del gating per poter caricare selettivamente nel byte superiore o inferiore.
Il risultato di provare a diventare little endian è che hai bisogno di più silicio (switch e gate) o di più operazioni.
In altre parole, in termini di controtendenza ai vecchi tempi, hai ottenuto più botto per la maggior parte delle prestazioni e la più piccola area di silicio.
In questi giorni, queste considerazioni e praticamente irrilevanti, ma cose come il riempimento della pipeline possono ancora essere un po 'un grosso problema.
Quando si tratta di scrivere s / w, la vita è spesso più facile quando si utilizza l'indirizzamento little endian.
(E i processori big endian tendono ad essere big endian in termini di byte ordering e little endian in termini di bit-in-byte. Ma alcuni processori sono strani e useranno il big endian bit e l'order order. rende la vita molto interessante per il progettista di h / w aggiungendo periferiche mappate in memoria ma non ha altra conseguenza per il programmatore.)
jimwise ha fatto un buon punto. C'è un altro problema, in little endian puoi fare quanto segue:
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
Più semplice per i programmatori che non sono interessati dall'evidente svantaggio delle posizioni scambiate nella memoria. Personalmente trovo che big endian sia inverso rispetto a ciò che è naturale :). 12 dovrebbe essere memorizzato e scritto come 21:)
I always wonder why someone would want to store the bytes in reverse order
Il numero decimale è scritto big endian. Inoltre, come lo scrivi in inglese. Si inizia con la cifra più significativa e la successiva più significativa o meno significativa. per es.
1234
sono mille, duecentotrentaquattro.
Questo è il modo in cui big endian viene talvolta chiamato l'ordine naturale.
In little endian, questo numero sarebbe uno, venti, trecentoquattromila.
Tuttavia, quando esegui operazioni aritmetiche come addizioni o sottrazioni, inizi con la fine.
1234
+ 0567
====
Inizi con 4 e 7, scrivi la cifra più bassa e ricorda il carry. Quindi aggiungi 3 e 6 ecc. Per aggiungere, sottrarre o confrontare, è più semplice da implementare, se hai già una logica per leggere la memoria in ordine, se i numeri sono invertiti.
Per supportare Big Endian in questo modo, è necessario che la logica legga la memoria al contrario o che si disponga del processo RISC che funziona solo sui registri. ;)
Gran parte del design Intel x86 / Amd x64 è storico.
Big-endian è utile per alcune operazioni (i paragoni tra "bignum" di ugelletta di lunghezza uguale alla primavere). Little-endian per gli altri (aggiungendo due "bignum", forse). Alla fine, dipende da cosa è stato configurato l'hardware della CPU, di solito è uno o l'altro (alcuni chip MIPS erano, IIRC, commutabili all'avvio per essere LE o BE).
Quando sono coinvolti solo lo storage e il trasferimento con lunghezze variabili, ma non c'è aritmetica con più valori, in genere LE è più facile da scrivere, mentre BE è più facile da leggere.
Prendiamo una conversione int-to-string (e ritorno) come esempio specifico.
int val_int = 841;
char val_str[] = "841";
Quando l'int viene convertito nella stringa, la cifra meno significativa è più facile da estrarre rispetto alla cifra più significativa. Tutto può essere fatto in un semplice ciclo con una semplice condizione di fine.
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = 'val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
Ora prova lo stesso nell'ordine BE. Di solito hai bisogno di un altro divisore che abbia la massima potenza di 10 per il numero specifico (qui 100). Devi prima trovarlo, ovviamente. Molto altro da fare.
La conversione da stringa a int è più facile da fare in BE, quando viene eseguita come operazione di scrittura inversa. Scrivi memorizza la cifra più significativa per ultima, quindi dovrebbe essere prima letto.
int val_int = 841;
char val_str[] = "841";
Ora fai lo stesso nell'ordine LE. Ancora una volta, avresti bisogno di un fattore aggiuntivo che inizi con 1 e venga moltiplicato per 10 per ogni cifra.
Quindi di solito preferisco usare BE per la memorizzazione, perché un valore è scritto esattamente una volta, ma letto almeno una volta e forse molte volte. Per la sua struttura più semplice, di solito uso anche il percorso per convertire in LE e quindi invertire il risultato, anche se scrive il valore una seconda volta.
Un altro esempio di memoria BE potrebbe essere la codifica UTF-8 e molti altri.
Leggi altre domande sui tag architecture storage advantages endianness