web-dev-qa-db-fra.com

Quelle est la finalité des littéraux binaires en C++ 14?

J'ai essayé de faire des recherches, mais je n'ai pas trouvé grand chose à propos des littéraux binaires et de l'endianisme. Les littéraux binaires sont-ils little-endian, big-endian ou autre chose (comme faire correspondre la plate-forme cible)?

Par exemple, quelle est la valeur décimale de 0b0111? Est-ce 7? Spécifique à la plateforme? Autre chose? Edit: J'ai pris une mauvaise valeur de 7 car elle est représentée dans un octet. La question a été suffisamment répondue malgré ce fait.

Un peu d’arrière-plan: en gros, j’essaie de déterminer la valeur des bits les moins significatifs, et le masquer avec des littéraux binaires semblait être une bonne solution… mais seulement s’il existe une garantie quant à l’endianisme.

40
Levi Morrison

Réponse courte: il n'y en a pas un .

Réponse longue: L'endianité n'est jamais exposée directement dans le code, à moins que vous n'essayiez vraiment de l'extraire (par exemple, en utilisant des astuces de pointeur). 0b0111 est 7, ce sont les mêmes règles que hex, écrit

int i = 0xAA77;

ne veut pas dire 0x77AA sur certaines plateformes, ce serait absurde. Où iraient les 0 supplémentaires manquants avec les ints 32 bits? Est-ce qu'ils seraient rembourrés sur le devant, puis le tout basculé en 0x77AA0000, ou seraient-ils ajoutés après? Je n'ai aucune idée de ce à quoi quelqu'un pourrait s'attendre si c'était le cas.

Le fait est que C++ ne fait aucune hypothèse sur l’endianité de la machine. Si vous écrivez du code en utilisant des primitives et les littéraux qu’elle fournit, le comportement sera le même d’une machine à une autre (à moins que vous ne commenciez à contourner le système de type, qui vous devrez peut-être faire)

Pour adresser votre mise à jour: le nombre sera celui que vous écrirez. Les bits ne seront pas réorganisés ou quoi que ce soit, le bit le plus significatif est à gauche et le bit le moins significatif est à droite.


Il semble y avoir un malentendu ici sur ce qu'est l’endianisme . Endianness fait référence à la manière dont octets sont ordonnés en mémoire et à la manière dont ils doivent être interprétés. Si je vous ai donné le chiffre "4172" et que j'ai dit "s'il s'agit de quatre mille cent soixante-douze, quelle est la finalité", vous ne pouvez pas vraiment donner de réponse car la question n'a pas de sens. ( Certains avancent que le chiffre le plus grand à gauche signifie big endian, mais sans adresse mémoire, la question de l’endianisme n’est ni susceptible de réponse ni pertinente ). Ceci est juste un nombre, il n'y a pas d'octets à interpréter, il n'y a pas d'adresses mémoire. En supposant une représentation entière sur 4 octets, les octets qui lui correspondent sont les suivants:

        low address ----> high address
Big endian:    00 00 10 4c
Little endian: 4c 10 00 00

ainsi, étant donné que l’un ou l’autre de ces critères est indiqué, "c’est la représentation interne de l’ordinateur sur 4172", vous pouvez déterminer s’il s’agit d’un petit ou d’un grand endian.

Alors considérons maintenant votre littéral binaire 0b0111, ces 4 bits représentent un nybble et peuvent être stockés comme suit:

              low ---> high
Big endian:    00 00 00 07
Little endian: 07 00 00 00

Mais vous n'avez pas à vous en soucier, car cela est également géré par le matériel. Le langage indique que le compilateur lit de gauche à droite, le bit le plus significatif au bit le moins significatif.

L'endianisme ne concerne pas des bits individuels . Étant donné qu'un octet est de 8 bits, si je vous remets 0b00000111 et dites "est-ce un petit ou un grand endian?" encore une fois, vous ne pouvez pas dire parce que vous n'avez qu'un octet. L'endianisme ne réordonne pas les bits dans un octet, il fait référence à la réorganisation d'octets entiers (sauf si vous avez bien sûr des octets d'un bit).

Vous n'avez pas à vous soucier de ce que votre ordinateur utilise en interne. 0b0111 vous évite simplement d'avoir à écrire des choses comme

unsigned int mask = 7 // only keep the lowest 3 bits

en écrivant

unsigned int mask = 0b0111;

Sans avoir besoin de commenter en expliquant la signification du nombre.

70
Ryan Haining

Tous les littéraux entiers, y compris les binaires, sont interprétés de la même façon que nous lisons normalement les nombres (le chiffre le plus à gauche étant le plus significatif).

Le standard C++ garantit la même interprétation des littéraux sans avoir à se préoccuper de l'environnement spécifique dans lequel vous vous trouvez. Ainsi, vous n'avez pas à vous soucier de l'environnement dans ce contexte.

Votre exemple de 0b0111 est toujours égal à sept.

La norme C++ n'utilise pas de termes de finalité en ce qui concerne les littéraux numériques. Au lieu de cela, il décrit simplement que les littéraux ont une interprétation cohérente et que cette interprétation est celle à laquelle on pourrait s’attendre.

Standard C++ - Littéraux entiers - 2.14.2 - paragraphe 1

Un littéral entier est une séquence de chiffres qui n'a pas de période ni de partie exposant , Avec des séparations facultatives séparées par des guillemets ignorés Lors de la détermination de sa valeur. Un littéral entier peut avoir un préfixe qui Spécifie sa base et un suffixe qui spécifie son type. Le premier chiffre lexical De la suite de chiffres est le plus significatif. Un littéral entier binaire (Base deux) commence par 0b ou 0B et consiste en Une séquence de chiffres binaires. Un littéral entier octal (base huit) Commence par le chiffre 0 et consiste en une séquence de chiffres octaux. Un littéral entier décimal (base dix) commence par un chiffre autre que 0 et consiste en une séquence de chiffres décimaux. Un entier hexadécimal Littéral (base seize) commence par 0x ou 0X et consiste en une séquence De chiffres hexadécimaux, comprenant les chiffres décimaux et les Lettres a à f et A jusqu'à F avec décimales de dix à quinze. [Exemple: le nombre douze peut être écrit 12, 014, 0XC ou 0b1100. Les littéraux 1048576, 1’048’576, 0X100000, 0x10’0000 et 0’004’000’000 ont tous la même valeur. - fin exemple]

Wikipedia décrit ce qu'est l'endianité et utilise notre système de numérotation comme exemple pour comprendre big-endian.

Les termes endian et endianness font référence à la convention utilisée par Pour interpréter les octets constituant un mot de données lorsque ces octets sont stockés Dans la mémoire de l'ordinateur.

Les systèmes big-endian stockent l'octet le plus significatif d'un mot dans la plus petite adresse Et l'octet le moins significatif est stocké dans la plus grande adresse (voir aussi Bit le plus significatif) . Les systèmes Peu-endians, en revanche, stockent l'octet le moins significatif dans la plus petite adresse .

Un exemple de finalité consiste à penser à la manière dont un nombre décimal est écrit Et lu en notation de valeur de position. En supposant un système d'écriture Où les nombres sont écrits de gauche à droite, la position la plus à gauche est Analogue à la plus petite adresse de mémoire utilisée et la position la plus à droite À la plus grande. Par exemple, le nombre cent vingt-trois Est écrit 1 2 3, avec la position des centaines la plus à gauche. Toute personne qui lit Ce nombre sait également que le chiffre le plus à gauche a la plus grande place . Ceci est un exemple de convention big-endian suivie dans la vie quotidienne .

Dans ce contexte, nous considérons qu'un chiffre d'un littéral entier est un "octet d'un mot" et que le mot est le littéral lui-même. En outre, le caractère le plus à gauche d'un littéral est considéré comme ayant la plus petite adresse.

Avec le 1234 littéral, les chiffres un, deux, trois et quatre sont les "octets d'un mot", et 1234 est le "mot". Avec le littéral binaire 0b0111, les chiffres zéro, un, un et un sont les "octets d'un mot" et le mot est 0111.

Cette considération nous permet de comprendre l’endianisme dans le contexte du langage C++ et montre que les littéraux entiers sont similaires à "big-endian".

39
Michael Gazonda

Il vous manque la distinction entre finalité telle qu’elle est écrite dans le code source et finalité telle que représentée dans le code objet. La réponse pour chacun d'eux n'est pas surprenante: les littéraux de code source sont bigendiens, car c'est ainsi que les humains les lisent. Dans le code objet, ils sont écrits quelle que soit la cible.

Puisqu’un octet est par définition la plus petite unité d’accès mémoire, je ne pense pas qu’il serait même possible d’attribuer une finalité à une représentation interne de bits dans un octet - le seul moyen de découvrir la finalité pour les grands nombres (intentionnellement ou par inadvertance). par surprise) est en y accédant de manière fragmentée, et l’octet est par définition la plus petite unité de stockage accessible.

10
jthill

Les langages C/C++ ne se soucient pas de l’endianité des entiers multi-octets. Les compilateurs C/C++ le font. Les compilateurs analysent votre code source et génèrent du code machine pour la plate-forme cible spécifique. Le compilateur, en général, stocke les littéraux entiers de la même manière qu'il stocke un entier; de sorte que les instructions de la CPU cible prennent directement en charge leur lecture et leur écriture en mémoire.

Le compilateur prend en charge les différences entre les plates-formes cibles afin que vous n'ayez pas à le faire.

Le seul moment où vous devez vous soucier de l’endianisme est de partager des valeurs binaires avec d’autres systèmes ayant un ordre d’octet différent. système sur lequel votre code est exécuté. 

7
Theron W Genaux

Une image vaut parfois plus que mille mots.

 source vs. memory endianness

3
Zoltan Tirinda

La finalité est définie par l'implémentation. La norme garantit que chaque objet a une représentation d'objet sous la forme d'un tableau de char et unsigned char, avec lequel vous pouvez travailler en appelant memcpy() ou memcmp(). En C++ 17, il est légal de reinterpret_cast un pointeur ou une référence à n'importe quel type d'objet (pas un pointeur sur void, un pointeur sur une fonction ou nullptr) sur un pointeur sur char, unsigned char ou std::byte, qui sont des alias valides pour n'importe quel type d'objet.

Ce que les gens veulent dire quand ils parlent d’endianisme, c’est l’ordre des octets dans cette représentation d’objet. Par exemple, si vous déclarez unsigned char int_bytes[sizeof(int)] = {1}; et int i; puis memcpy( &i, int_bytes, sizeof(i)); obtenez-vous 0x01, 0x01000000, 0x0100, 0x0100000000000000 ou autre chose? La réponse est oui. Il existe des mises en œuvre dans le monde réel qui produisent chacun de ces résultats et qui sont toutes conformes à la norme. La raison en est que le compilateur peut utiliser le format natif de la CPU.

Cela se produit le plus souvent lorsqu'un programme doit envoyer ou recevoir des données sur Internet, où toutes les normes définissent les données qui doivent être transmises dans l'ordre big-endian, sur un processeur little-endian comme le x86. Certaines bibliothèques de réseau spécifient donc si des arguments et des champs de structures particuliers doivent être stockés dans l’ordre de l’hôte ou du réseau.

Le langage vous permet de vous tirer dans le pied en twiddant arbitrairement les éléments d’une représentation d’objet, mais il peut vous donner une représentation trap , ce qui peut provoquer un comportement indéfini si vous essayez de l’utiliser plus tard. (Cela pourrait signifier, par exemple, la réécriture d'une table de fonction virtuelle pour injecter du code arbitraire.) L'en-tête <type_traits> contient plusieurs modèles pour vérifier s'il est prudent de faire les choses avec une représentation d'objet. Vous pouvez copier un objet sur un autre du même type avec memcpy( &dest, &src, sizeof(dest) ) si ce type is_trivially_copyable. Vous pouvez effectuer une copie dans de la mémoire non initialisée correctement alignée si elle est is_trivially_move_constructible. Vous pouvez tester si deux objets du même type sont identiques à memcmp( &a, &b, sizeof(a) ) et correctement hachés un objet en appliquant une fonction de hachage aux octets de sa représentation d'objet si le type has_unique_object_representations. Un type intégral n'a pas de représentation de piège, etc. Cependant, dans la plupart des cas, si vous effectuez des opérations sur des représentations d’objet où l’endianisme est important, vous dites au compilateur de supposer que vous savez ce que vous faites et que votre code ne sera pas portable.

Comme d'autres l'ont mentionné, les littéraux binaires sont écrits en commençant par le chiffre le plus significatif, comme les littéraux décimaux, octaux ou hexidécimaux. Ceci est différent de endianness et n'affectera pas la nécessité d'appeler ntohs() sur le numéro de port à partir d'un en-tête TCP lu à partir d'Internet.

0
Davislor