58 votes

Pourquoi n'y a-t-il pas de modificateur de finalité en C ++ comme pour la signature?

(Je pense que cette question pourrait s'appliquer à de nombreux langages à typage, mais j'ai choisi d'utiliser le C++ comme un exemple.)

Pourquoi n'est-il pas moyen de simplement écrire:

struct foo {
    little int x;   // little-endian
    big long int y; // big-endian
    short z;        // native endianness
};

pour spécifier le boutisme de certains membres, les variables et les paramètres?

La comparaison de ce paramètre

Je comprends que le type d'une variable détermine non seulement le nombre d'octets utilisés pour stocker une valeur, mais aussi la façon dont ces octets sont interprétés lors de l'exécution des calculs.

Par exemple, ces deux déclarations chaque allouer un octet, et pour les deux octets, chaque séquence 8-bit est une valeur valide:

signed char s;
unsigned char u;

mais la même séquence binaire pourrait être interprétée différemment, par exemple, 11111111 signifierait -1 lorsqu' s mais 255 lorsqu' u. Lorsque signés et non signés, les variables sont impliquées dans le même calcul, le compilateur (pour la plupart) prend en charge les conversions.

Dans ma compréhension, endianness est juste une variation du même principe: une interprétation différente d'un modèle binaire basée sur la compilation de l'information à propos de la mémoire dans lequel elle sera enregistrée.

Il semble évident pour avoir cette fonctionnalité dans un typée de la langue qui permet la programmation de bas niveau. Cependant, ce n'est pas une partie de C, C++ ou tout autre langage que je connais, et je n'ai pas trouver toutes les discussions sur ce sujet en ligne.

Mise à jour

Je vais essayer de résumer certains plats à emporter à partir des nombreux commentaires que j'ai reçu dans la première heure après avoir poser la question:

  1. ce paramètre est strictement binaire (soit signé ou non signé), et sera toujours, contrairement à l'endianness, qui a également deux variantes (grand et petit), mais également des variantes moins connues comme mixte/moyen-endian. De nouvelles variantes peuvent être inventé dans le futur.
  2. endianness questions lors de l'accès à plusieurs valeurs d'octets octet-sage. Il ya de nombreux aspects-delà de l'endianness qui affectent la mémoire mise en page multi-octets, de façon à ce type d'accès est surtout découragé.
  3. C++ a pour objectif d'une machine abstraite et réduire le nombre d'hypothèses à propos de la mise en œuvre. Cette machine abstraite ne dispose pas de toute l'endianness.

Aussi, maintenant, je me rends compte que ce paramètre et de stockage ne sont pas d'une parfaite analogie, parce que:

  • endianness définit seulement la façon dont quelque chose est représenté comme une séquence binaire, mais maintenant ce qui peut être représenté. Les deux big int et little int aurait exactement la même gamme de valeur.
  • ce paramètre définit la façon dont les bits les valeurs réelles et de mapper les uns aux autres, mais aussi sur ce qui peut être représentée, par exemple, -3 ne peuvent pas être représentés par un unsigned char et (en supposant que l' char a 8 bits) 130 peut pas être représenté par un signed char.

De sorte que la modification de l'endianness de certaines variables ne serait jamais changer le comportement du programme (sauf pour les byte-sage d'accès), alors un changement de ce paramètre habituellement.

53voto

YSC Points 3386

Ce que dit la norme

[intro.abstract]/1:

La sémantique des descriptions dans le présent document de définir un paramétrée de façon non déterministe machine abstraite. Ce document n'impose aucune exigence sur la structure de la conformité des mises en œuvre. En particulier, ils n'ont pas besoin de copier ou de reproduire la structure de la machine abstraite. Plutôt, conforme implémentations sont nécessaires pour émuler (seulement) les comportements observables de la machine abstraite comme expliqué ci-dessous.

C++ n'a pas pu définir un endianness qualificatif, car il n'a aucune notion de l'endianness.

Discussion

À propos de la différence entre signness et endianness, OP écrit

Dans ma compréhension, endianness est juste une variation du même principe [(signness)]: une interprétation différente d'un modèle binaire basée sur la compilation de l'information à propos de la mémoire dans lequel elle sera enregistrée.

Je dirais signness les deux ont une sémantique et un représentant de l'aspect1. Ce [intro.abstract]/1 implique que C++ ne se soucient que de la sémantique, et de ne jamais les adresses de la façon dont un signé nombre devrait être représenté dans la mémoire2. En fait, "bit de signe" apparaît une seule fois dans le C++, les specs, et de se référer à une mise en valeur définie.
D'autre part, endianness seulement un représentant aspect: endianness transmet pas de sens.

Avec C++20, std::endian s'affiche. Il est toujours de mise en œuvre défini, mais permettez-nous de tester l'-boutiste de l'hôte sans dépendre des vieux trucs basés sur un comportement indéterminé.


1) aspect Sémantique: un entier signé peut représenter des valeurs au-dessous de zéro; le représentant aspect: l'un a besoin, par exemple, en réserver un peu pour transmettre le positif/négatif signe.
2) Dans la même veine, le C++ ne jamais décrire la manière dont un nombre à virgule flottante doit être représentée, IEEE-754 est souvent utilisé, mais c'est un choix fait par la mise en œuvre, en tout cas, appliquée par le standard: [basic.fundamental]/8 "La valeur de la représentation de virgule flottante de types définis par l'implémentation".

36voto

Useless Points 18909

En plus de YSC répondre, prenons votre exemple de code, et d'envisager ce qu'il pourrait viser à atteindre

struct foo {
    little int x;   // little-endian
    big long int y; // big-endian
    short z;        // native endianness
};

Vous pourriez espérer que ce serait exactement spécifier un modèle de données indépendants de l'architecture d'échange de fichiers, réseau, peu importe)

Mais cela ne peut pas travailler, parce que plusieurs choses sont encore indéterminée:

  • type de données de la taille: vous devez utiliser little int32_t, big int64_t et int16_t respectivement, si c'est ce que vous voulez
  • de rembourrage et de l'alignement, qui ne peut être contrôlé strictement à l'intérieur de la langue: #pragma ou __attribute__((packed)) ou de quelque autre compilateur extension spécifique
  • format réel (1s ou 2s-complément de ce paramètre, en virgule flottante type de mise en page, piège représentations)

Alternativement, vous pouvez simplement refléter le boutisme de certains matériels - mais big et little ne couvrent pas toutes les possibilités ici (juste les deux plus courantes).

Donc, la proposition est incomplète (il ne distingue pas toutes les mesures raisonnables octet de commande d'arrangements), inefficace (il ne permet pas d'obtenir ce qu'il entend), et a des inconvénients:

  • Performance

    La modification de la boutisme d'une variable à partir de la maternelle de l'octet de commande soit désactiver l'arithmétique, les comparaisons etc (puisque le matériel ne peut pas effectuer correctement sur ce type), ou doit silencieusement injecter plus de code, la création de mode natif commandé temporaires pour travailler sur.

    L'argument ici n'est pas que manuellement la conversion vers/à partir des octets de commande est plus rapide, c'est que le contrôle de ce qu'il soit explicitement fait, il est plus facile de réduire le nombre de conversions inutiles, et beaucoup plus facile de raisonner sur la façon dont le code va se comporter, que si les conversions implicites.

  • La complexité

    Tout surchargé ou spécialisés pour les types d'entiers a maintenant besoin de deux fois autant de versions, pour faire face à l'événement rare que celui-ci est transmis à un non-natif-boutisme de la valeur. Même si c'est juste un transfert de wrapper (avec un couple de jette pour traduire vers/à partir de natif de la commande), il reste encore beaucoup de code pour aucun bénéfice perceptible.

Le dernier argument contre la modification de la langue à l'appui de cette est que vous pouvez le faire facilement dans le code. La modification de la syntaxe de la langue est une grosse affaire, et n'offre aucun avantage évident par rapport à quelque chose comme un type wrapper:

// store T with reversed byte order
template <typename T>
class Reversed {
    T val_;
    static T reverse(T); // platform-specific implementation
public:
    explicit Reversed(T t) : val_(reverse(t)) {}
    Reversed(Reversed const &other) : val_(other.val_) {}
    // assignment, move, arithmetic, comparison etc. etc.
    operator T () const { return reverse(val_); }
};

3voto

Chad Farmer Points 41

Les entiers (comme un concept mathématique), le concept de nombres positifs et négatifs. Cette notion abstraite de signe a un certain nombre de différentes mises en œuvre dans le matériel.

Boutisme n'est pas un concept mathématique. Little-endian est une mise en œuvre matérielle astuce pour améliorer la performance de multi-octets deux-complément de l'arithmétique des nombres entiers sur un microprocesseur avec 16 ou 32 bits de registres et un 8-bit bus mémoire. Sa création a nécessité l'utilisation du terme big-endian pour décrire tout ce qui avait le même ordre d'octet dans des registres et de la mémoire.

Le C machine abstraite comprend le concept d'entiers signés et non signés, sans plus de détails, -- sans nécessiter deux-complément de l'arithmétique, les octets de 8 bits ou comment stocker un nombre binaire en mémoire.

PS: je suis d'accord que les données binaires de compatibilité sur le net ou dans la mémoire/stockage est un PIA.

2voto

D Dowling Points 114

C'est une bonne question et j'ai souvent pensé que quelque chose comme cela serait utile. Cependant, vous devez vous rappeler que C vise l'indépendance de plate-forme et de l'endianness est important quand une structure comme celle-ci est convertie en sous-jacent disposition de la mémoire. Cette conversion peut se produire lorsque vous lancez un u_int8_t tampon dans un int par exemple. Alors qu'un endianness modificateur de l'air soigné, le programmeur doit toujours envisager d'autres différences de plate-forme telle que int taille et la structure de l'alignement et de l'emballage. Pour une programmation défensive quand vous voulez trouver le grain de contrôle sur la façon dont certaines des variables ou des structures sont représentées dans une mémoire tampon, alors il est préférable de code explicite des fonctions de conversion et de laisser le compilateur optimiseur de générer le code le plus efficace pour chaque plate-forme.

2voto

Réponse courte: si elle ne doit pas être possible d'utiliser des objets dans des expressions arithmétiques (sans opérateurs surchargés) impliquant des entiers, alors ces objets ne doivent pas être les types d'entiers. Et il n'y a aucun point en permettant l'addition et de la multiplication des big-endian et little-endian ints dans la même expression.

Plus De Réponse:

Comme quelqu'un l'a mentionné, endianness est spécifiques au processeur. Ce qui veut vraiment dire que c'est la façon dont les nombres sont représentés lorsqu'ils sont utilisés comme des numéros dans le langage machine (comme les adresses et les opérandes et les résultats des opérations arithmétiques).

De la même est "une sorte de" vrai de la signalisation. Mais pas au même degré. La Conversion de la langue, de la sémantique panneaux de signalisation pour le processeur accepté de signalisation est quelque chose qui doit être fait pour utiliser les nombres en chiffres. La Conversion de big-endian pour little-endian et l'inverse est quelque chose qui doit être fait pour utiliser les nombres comme des données (les envoyer sur le réseau ou à représenter les métadonnées sur les données envoyées sur le réseau telles que la charge utile longueurs).

Cela dit, cette décision semble être principalement tirée par les cas d'utilisation. Le revers de la médaille est qu'il y a une bonne raison pragmatique à ignorer certains cas d'utilisation. Le pragmatisme découle du fait que l'endianness, la conversion est plus cher que la plupart des opérations arithmétiques.

Si une langue a de la sémantique pour garder les numéros de little-endian, il serait de permettre aux développeurs de tirer eux-mêmes dans le pied en forçant peu-boutisme de nombres dans un programme qui est de l'arithmétique. Si elle est développée sur un little-endian machine, cette application de l'endianness serait un no-op. Mais lorsqu'ils sont portés à un big-endian machine, il y aurait beaucoup de ralentissements imprévus. Et si les variables en question ont été utilisés à la fois pour l'arithmétique et en tant que réseau de données, il serait de rendre le code entièrement non-portable.

N'ayant pas de ces endian sémantique ou de les forcer à être explicitement au compilateur spécifique des forces les développeurs à passer par le mental étape de la pensée de l'numéros comme "lu" ou "écrit" vers/à partir du format de réseau. Cela permettrait de rendre le code qui convertit en arrière et en avant entre le réseau et l'ordre des octets de l'hôte, au moyen d'opérations arithmétiques, lourdes et moins de chances d'être le moyen privilégié de l'écriture par un paresseux développeur.

Et étant donné que le développement est une activité humaine, faisant de mauvais choix mal à l'aise est une Bonne Chose(TM).

Edit: voici un exemple de comment cela peut aller mal: Supposons qu' little_endian_int32 et big_endian_int32 types sont introduits. Ensuite, little_endian_int32(7) % big_endian_int32(5) est une expression constante. Quel est le résultat? Les chiffres obtenir implicitement converti dans le format natif? Si non, quel est le type du résultat? Pire encore, quelle est la valeur du résultat (qui dans ce cas devrait probablement être le même sur toutes les machines)?

Encore une fois, si le multi-octets sont utilisés comme de simples données, des tableaux de char sont tout aussi bonnes. Même s'ils sont "ports" (qui sont vraiment à la recherche des valeurs dans des tables de hachages), ils sont juste des séquences d'octets plutôt que les types d'entiers (sur lequel on peut effectuer des calculs arithmétiques).

Maintenant, si vous limitez le permis opérations arithmétiques sur explicite-endian numéros aux seules opérations autorisées pour les types pointeur, alors vous pourriez avoir un meilleur dossier pour la prévisibilité. Ensuite, myPort + 5 fait réellement sens, même si myPort est déclaré en tant que quelque chose comme little_endian_int16 sur une machine big endian. De même pour lastPortInRange - firstPortInRange + 1. Si l'arithmétique fonctionne comme il le fait pour les types de pointeur, alors ce serait de faire ce que vous attendez, mais firstPort * 10000 serait illégal.

Alors, bien sûr, de vous lancer dans l'argument de savoir si la fonctionnalité de la météorisation est justifiée par tout avantage possible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X