Little Endian vs Big Endian en C

Little Endian vs Big Endian en C

Dans ce tutoriel, nous discuterons du concept de Endianness dans l'ordinateur. Endian signifie commander des octets. Deux types d'endianité sont présents dans l'ordinateur: Big Indian et Little Indian. Lorsque les données sont stockées en mémoire, les bits sont stockés octets par octet en octet. L'octet est une unité de données de mémoire qui se compose d'un groupe de 8 bits. La mémoire de l'ordinateur est adhéchable aux octets, donc un seul octet peut être présent à l'intérieur de l'emplacement de la mémoire. Lorsque les données ne sont qu'un octet, cet octet est stocké dans un seul emplacement de mémoire. Mais lorsque les données sont supérieures à un octet, ces octets peuvent être stockés dans la mémoire de deux manières différentes.

Exemples à comprendre:

int x = 513;

La représentation binaire de deux octets de 513 est 0000001000000001.

MSB LSB
00000010 00000001

La mémoire est adressable. Un octet est stocké dans un seul emplacement de mémoire et cet emplacement de mémoire a une adresse. Si un octet est stocké dans l'adresse «A», l'octet suivant est stocké dans l'adresse suivante qui est «A + 1», et ainsi de suite. Maintenant, les octets peuvent être stockés dans la mémoire des octets les plus à gauche aux octets les plus droits ou des octets les plus droits aux octets les plus à gauche.

Ici, l'adresse de mémoire de départ est «A». Ainsi, si les octets sont stockés dans la mémoire des octets les plus à gauche aux octets les plus droits, le plus d'octet gauche "00000010"Est stocké dans l'emplacement de la mémoire" A "et l'octet le plus à droite"00000001"Est stocké dans l'emplacement de la mémoire" A + 1 ".

A - 1
un 00000010
A + 1 00000001
A + 2

Qu'est-ce que Big Endian?

Lorsque l'octet le plus significatif est présent dans le plus petit emplacement de mémoire, l'octet suivant est stocké dans l'adresse mémoire suivante, et ainsi de suite. Ce type d'ordre d'octet est appelé «Big Endian».

Si les octets sont stockés dans la mémoire des octets les plus à droite aux octets les plus à gauche, le droit le plus octet "00000001"Est stocké dans l'emplacement de la mémoire" A "et l'octet le plus gauche"00000010"Est stocké dans l'emplacement de la mémoire" A + 1 ".

A - 1
un 00000001
A + 1 00000010
A + 2

Qu'est-ce que le petit endian?

Lorsque l'octet le moins significatif est stocké dans le plus petit emplacement de mémoire, l'octet précédent est stocké dans l'adresse mémoire suivante, etc. Ce type d'ordre d'octet est appelé «petit endian».

A - 1
un 00000010
A + 1 00000001
A + 2

Nous pouvons vérifier si un ordinateur est Big Endian ou Little Endian en utilisant les exemples de code C suivant:

Exemple 1:

#inclure
void endian ()
int x = 1;
char * a = (char *) & x;
if (a [0] == 1)
printf ("Little Endian \ n");
autre
printf ("big endian \ n");

int main()
printf ("Machine Endianness =>");
Endian ();
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple1.C -o Exemple1
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./Exemple 1
Machine Endianness => Little Endian
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Ici, si l'entier est de deux octets, la représentation binaire de 1 est de 00000000 00000001. Nous convertissons le pointeur en un pointeur de charbon qui fait un octet de long. Donc, si nous accédons au premier emplacement de mémoire et que nous obtenons la valeur de 1, cela signifie que l'octet le plus à droite est stocké dans l'emplacement de la mémoire le plus bas. C'est alors une petite machine endian. Sinon, ce sera une grande machine endian.

Exemple 2:

#inclure
Union Endian
int i;
char c [sizeof (int)];
;
int main()
Union endian u;
u.i = 1;
si tu.c [0] == 1)
printf ("Little Endian \ n");
autre
printf ("big endian \ n");
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple2.C -o Exemple2
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple2
Petit endian
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Dans cet exemple, nous utilisons Union. Si 1 est stocké dans le 0e Emplacement du tableau, la machine doit être peu endian. Sinon, la machine sera grande endian.

Problème dans Endianness

Un ordinateur stocke et récupère les données dans la même endianness dans laquelle le résultat est le même. Le problème se pose dans Endianness lorsque les données transfèrent d'une machine à une autre machine. Si les deux machines sont en sexe d'octets différentes, cela signifie qu'un ordinateur utilise le Big Endian et un autre ordinateur utilise le petit endian. Lorsque les données se transfèrent de l'une à l'autre, les problèmes réels surviennent. Ce problème s'appelle Nuxi Problem: la chaîne «Unix» peut ressembler à «Nuxi» sur une machine avec un sexe d'octet différent.

Un autre problème apparaît lorsque nous utilisons TypeCast dans notre programme. Par exemple: si nous créons un tableau de caractères d'ARR [4] et le cast à un int de taille 4 octet, nous obtiendrons un résultat différent sur une machine endian différente. En discutons dans l'exemple suivant.

Exemple 3:

#inclure
int main()

char arr [4] = 0x01, 0x00,0x00,0x00;
int x = * (int *) arr;
printf ("0x% x \ n", x);
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple3.C -o Exemple3
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple3
0x1
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Dans cet exemple, «arr» est un tableau de caractères. Nous le démunis à un entier de 4 octets x. Si nous compilons le programme dans une petite machine endian, nous obtenons la valeur de 0x00000001. Mais si nous compilons le programme dans une grande machine endian, nous obtenons la valeur de 0x01000000.

0x01 0x00 0x00 0x00
arr [0] arr [1] arr [2] arr [3]

Exemple 4:

#inclure
int main()

char arr [4] = 0x00, 0x00,0x00,0x01;
int x = * (int *) arr;
printf ("0x% x \ n", x);
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple4.C -o Exemple4
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple4
0x1000000
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Cet exemple est le même que dans l'exemple 3. Changez uniquement la valeur pour le comprendre de manière plus simple. Si nous compilons le programme dans une petite machine endian, nous obtenons la valeur de 0x01000000. Mais si nous compilons le programme dans une grande machine endian, nous obtenons la valeur de 0x00000001.

0x00 0x00 0x00 0x01
arr [0] arr [1] arr [2] arr [3]

Lorsque les données sont transférées d'une machine endian vers une autre machine endian, nous devons échanger les données en conséquence. Maintenant, voyons comment échanger les données dans les exemples suivants.

Exemple 5:

Dans cet exemple, nous utilisons le fonctionnement du bit pour échanger les données. Dans le sens du bit, il n'y a aucun effet de Endianness.

#inclure
#inclure
uint32_t byteswap (valeur uint32_t)

Uint32_t result = 0;
Résultat = résultat | (valeur et 0x000000ff) << 24;
Résultat = résultat | (valeur et 0x0000ff00) << 8;
Résultat = résultat | (valeur et 0x00ff0000) >> 8;
Résultat = résultat | (valeur et 0xff000000) >> 24;
Résultat de retour;

int main()

uint32_t data = 0x44445555;
uint32_t resultData = 0;
resultData = byteswap (data);
printf ("data d'origine => 0x% x \ n", données);
printf ("Données converti => 0x% x \ n", resultData);
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple3.C -o Exemple3
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple3
Données d'origine => 0x44455555
Données converties => 0x55554444
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Exemple 6:

Dans cet exemple, nous ferons la même chose que nous l'avons fait dans l'exemple 5. Mais ici, nous utilisons des macros au lieu de la fonction.

#inclure
#inclure
#define Change_endianness (a) ((((uint32_t) (a) & 0xff000000) >> 24) \

| (((uint32_t) (a) & 0x00ff0000) >> 8) \
| (((uint32_t) (a) & 0x0000ff00) << 8) \
| (((uint32_t) (a) & 0x000000ff) << 24))
int main()
uint32_t data = 0x44445555;
uint32_t resultData = 0;
resultData = change_endianness (data);
printf ("data d'origine => 0x% x \ n", données);
printf ("Données converti => 0x% x \ n", resultData);
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple6.C -o Exemple6
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple6
Données d'origine => 0x44455555
Données converties => 0x55554444
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Exemple 7:

Dans cet exemple, nous ferons la même chose que nous l'avons fait dans l'exemple précédent. Mais ici, nous utilisons Union.

#inclure
#inclure
Union de Typedef

uint32_t RawData;
uint8_t databuff [4];
Données brutes;
uint32_t Change_endianness (valeur uint32_t)

Changement RawData, original;
Original.u32rawdata = valeur;
// modifie la valeur
Changement.Databuff [0] = original.Databuff [3];
Changement.Databuff [1] = original.Databuff [2];
Changement.Databuff [2] = original.Databuff [1];
Changement.Databuff [3] = original.Databuff [0];
Retour (changer.Données brutes);

int main()
uint32_t data = 0x44445555;
uint32_t resultData = 0;
resultData = change_endianness (data);
printf ("data d'origine => 0x% x \ n", données);
printf ("Données converti => 0x% x \ n", resultData);
retour 0;

Sortir:

Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / ENIAN $ GCC Exemple5.C -o Exemple5
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $ ./ Exemple5
Données d'origine => 0x44455555
Données converties => 0x55554444
Somnath @ Somnath-VirtualBox: ~ / Desktop / C_Prog / Endian $

Lorsque nous envoyons les données sur le réseau, les données sont envoyées dans un format commun. Lorsque les données sont envoyées sur le réseau, nous ne connaissons pas l'endianisme de l'expéditeur et du récepteur. Donc, nous utilisons une commande spéciale qui est Big Endian. Cette commande est appelée «ordre du réseau».

Si l'expéditeur est une petite machine endian, il convertit les données en une grande commande endian avant d'envoyer les données. Si le récepteur est une petite machine endian, il convertit les données en un peu de format endian après avoir reçu les données.

Lorsque nous envoyons les données au réseau, les macros suivantes sont utilisées:

HTONS () - «Hôte du réseau court»

htonl () - «Hôte de réseau long»

ntohs () - «Réseau pour héberger court»

ntohl () - "Réseau d'héberger longtemps"

htons () - Cette macro est lue comme «hôte de la courte du réseau». Les octets d'une valeur non signée 16 bits doivent être réorganisés comme les suivants:

Ordre du processeur -> Commande réseau.

htonl () - Cette macro est lue comme «hôte de réseau long». Une valeur non signée 32 bits doit être réorganisée comme ce qui suit:

Ordre du processeur -> Commande réseau.

ntohs () - Cette macro est lue comme «réseau pour héberger court». Les octets d'une valeur non signée 16 bits doivent être réorganisés comme les suivants:

Ordre du réseau -> Ordre du processeur.

ntohl () - Cette macro est lue comme «réseau pour héberger longtemps». Une valeur non signée 32 bits doit être réorganisée comme ce qui suit:

Ordre du réseau -> Ordre du processeur.

Conclusion

Dans ce tutoriel, nous avons appris les détails sur Endianness dans l'ordinateur. Il semble que l'utilisation d'une approche d'endienne sur l'autre n'a aucun avantage. Ils sont tous deux encore utilisés par diverses architectures. La majorité des ordinateurs personnels et des ordinateurs portables utilisent aujourd'hui les processeurs à base de Little-Endan (et leurs clones), ce qui rend le petit endian comme architecture prédominante pour les ordinateurs de bureau.