De temps en temps, quelqu'un sur SO souligne que char
(aka 'octet') n'est pas nécessairement 8 bits .
Il semble que char
8 bits soit presque universel. J'aurais pensé que pour les plateformes grand public, il est nécessaire d'avoir un char
8 bits pour assurer sa viabilité sur le marché.
À la fois maintenant et historiquement, quelles plates-formes utilisent un char
qui n'est pas 8 bits, et pourquoi différeraient-ils des 8 bits "normaux"?
Lorsque vous écrivez du code et pensez à la prise en charge multiplateforme (par exemple pour les bibliothèques à usage général), quel genre de considération vaut-il la peine de donner aux plates-formes avec char
non-8 bits?
Dans le passé, je suis tombé sur des DSP Analog Devices pour lesquels char
est de 16 bits. Les DSP sont un peu une architecture de niche, je suppose. (Là encore, à l'époque, l'assembleur codé à la main battait facilement ce que les compilateurs C disponibles pouvaient faire, donc je n'ai pas vraiment acquis beaucoup d'expérience avec C sur cette plate-forme.)
char
est également en 16 bits sur les DSP Texas Instruments C54x, apparus par exemple dans OMAP2. Il existe d'autres DSP avec _ et $ 16 bits char
. Je pense que j'ai même entendu parler d'un DSP 24 bits, mais je ne me souviens pas de quoi, alors je l'ai peut-être imaginé.
Une autre considération est que les mandats POSIX CHAR_BIT == 8
. Donc, si vous utilisez POSIX, vous pouvez l'assumer. Si quelqu'un a besoin par la suite de porter votre code vers une implémentation proche de POSIX, il se trouve que les fonctions que vous utilisez mais d'une taille différente char
, c'est leur malchance.
En général, cependant, je pense qu'il est presque toujours plus facile de contourner le problème que d'y réfléchir. Tapez simplement CHAR_BIT
. Si vous voulez un type 8 bits exact, utilisez int8_t
. Votre code échouera bruyamment à compiler sur des implémentations qui n'en fournissent pas, au lieu d'utiliser silencieusement une taille que vous ne vous attendiez pas. À tout le moins, si je frappais un cas où j'avais une bonne raison de l'assumer, je l'affirmais.
Lorsque vous écrivez du code et pensez à la prise en charge multiplateforme (par exemple pour les bibliothèques à usage général), quel genre de considération vaut-il la peine de donner aux plates-formes avec un caractère non 8 bits?
Ce n'est pas tant qu'il "vaut la peine d'envisager" quelque chose car il respecte les règles. En C++, par exemple, la norme indique que tous les octets auront "au moins" 8 bits. Si votre code suppose que les octets ont exactement 8 bits, vous violez la norme.
Cela peut sembler idiot maintenant - " bien sûr tous les octets ont 8 bits!", Je vous entends dire. Mais beaucoup de gens très intelligents se sont appuyés sur des hypothèses qui n'étaient pas des garanties, puis tout s'est cassé. L'histoire regorge de tels exemples.
Par exemple, la plupart des développeurs du début des années 90 ont supposé qu'un délai de synchronisation du processeur particulier sans opération prenant un nombre fixe de cycles nécessiterait un temps d'horloge fixe, car la plupart des processeurs grand public étaient à peu près équivalents en puissance. Malheureusement, les ordinateurs se sont accélérés très rapidement. Cela a engendré la montée des boîtes avec des boutons "Turbo" - dont le but, ironiquement, était de ralentir l'ordinateur afin que les jeux utilisant la technique de temporisation puissent être joués à une vitesse raisonnable.
Un intervenant a demandé où, dans la norme, il est indiqué que char doit avoir au moins 8 bits. C'est dans la section 5.2.4.2.1. Cette section définit CHAR_BIT
, le nombre de bits dans la plus petite entité adressable, et a une valeur par défaut de 8. Il dit également:
Leurs valeurs définies par l'implémentation doivent être de magnitude égale ou supérieure (valeur absolue) à celles indiquées, avec le même signe.
Ainsi, tout nombre égal à 8 ou plus peut être remplacé par une implémentation dans CHAR_BIT
.
Les machines avec des architectures 36 bits ont 9 octets. Selon Wikipedia, les machines avec des architectures 36 bits comprennent:
Quelques-uns dont je suis au courant:
Il n'y a pas de code complètement portable. :-)
Oui, il peut y avoir différentes tailles d'octets/caractères. Oui, il peut y avoir des implémentations C/C++ pour les plates-formes avec des valeurs très inhabituelles de CHAR_BIT
et UCHAR_MAX
. Oui, il est parfois possible d'écrire du code qui ne dépend pas de la taille des caractères.
Cependant, presque tout code réel n'est pas autonome. Par exemple. vous écrivez peut-être un code qui envoie des messages binaires au réseau (le protocole n'est pas important). Vous pouvez définir des structures contenant les champs nécessaires. Vous devez alors le sérialiser. La simple copie binaire d'une structure dans un tampon de sortie n'est pas portable: en général, vous ne connaissez ni l'ordre des octets pour la plate-forme, ni l'alignement des membres de la structure, de sorte que la structure ne contient que les données, mais ne décrit pas la façon dont les données doivent être sérialisées .
D'accord. Vous pouvez effectuer des transformations d'ordre des octets et déplacer les membres de la structure (par exemple uint32_t
ou similaire) en utilisant memcpy
dans le tampon. Pourquoi memcpy
? Parce qu'il y a beaucoup de plates-formes où il n'est pas possible d'écrire en 32 bits (16 bits, 64 bits - pas de différence) lorsque l'adresse cible n'est pas alignée correctement.
Vous avez donc déjà fait beaucoup pour atteindre la portabilité.
Et maintenant, la dernière question. Nous avons un tampon. Les données qu'il contient sont envoyées au réseau TCP/IP. Un tel réseau suppose des octets de 8 bits. La question est: de quel type doit être le tampon? Si vos caractères sont 9 bits? S'ils sont en 16 bits? 24? Peut-être que chaque caractère correspond à un octet de 8 bits envoyé au réseau et que seuls 8 bits sont utilisés? Ou peut-être que plusieurs octets de réseau sont regroupés dans des caractères 24/16/9 bits? C'est une question, et il est difficile de croire qu'il existe une réponse unique qui convient à tous les cas. Beaucoup de choses dépendent de l'implémentation de socket pour la plate-forme cible.
Alors, de quoi je parle. Habituellement, le code peut être fait relativement facilement portable dans une certaine mesure. Il est très important de le faire si vous prévoyez d'utiliser le code sur différentes plates-formes. Cependant, améliorer la portabilité au-delà de cette mesure est une chose qui nécessite beaucoup d'efforts et donne souvent pe, car le vrai code dépend presque toujours d'un autre code (implémentation de socket dans l'exemple ci-dessus). Je suis sûr que pour environ 90% de la capacité du code à travailler sur des plates-formes avec des octets autres que 8 bits est presque inutile, car il utilise un environnement lié à 8 bits. Vérifiez simplement la taille de l'octet et effectuez l'assertion du temps de compilation. Vous devrez presque sûrement réécrire beaucoup pour une plate-forme très inhabituelle.
Mais si votre code est très "autonome" - pourquoi pas? Vous pouvez l'écrire d'une manière qui autorise différentes tailles d'octets.
De nombreuses puces DSP ont une char
de 16 ou 32 bits. TI fabrique régulièrement ces puces par exemple .
Il semble que vous pouvez toujours acheter un IM61 (c'est-à-dire un PDP-8 sur une puce) hors d'un entrepôt. C'est une architecture 12 bits.
Les langages de programmation C et C++, par exemple, définissent l'octet comme "unité de données adressable suffisamment grande pour contenir n'importe quel membre du jeu de caractères de base de l'environnement d'exécution" (article 3.6 de la norme C). Étant donné que le type de données intégral C char doit contenir au moins 8 bits (clause 5.2.4.2.1), un octet en C peut au moins contenir 256 valeurs différentes. Diverses implémentations de C et C++ définissent un octet sur 8, 9, 16, 32 ou 36 bits
Cité de http://en.wikipedia.org/wiki/Byte#History
Je ne suis pas sûr des autres langues.
http://en.wikipedia.org/wiki/IBM_7030_Stretch#Data_Formats
Définit un octet sur cette machine comme étant de longueur variable
La famille DEC PDP-8 avait un mot de 12 bits bien que vous utilisiez généralement 8 bits ASCII pour la sortie (sur un télétype principalement). Cependant, il y avait aussi un code de caractère à 6 bits qui vous permettait pour coder 2 caractères dans un seul mot 12 bits.
D'une part, les caractères Unicode sont plus longs que 8 bits. Comme quelqu'un l'a mentionné précédemment, la spécification C définit les types de données par leur taille minimale. Utilisez sizeof
et les valeurs dans limits.h
Si vous souhaitez interroger vos types de données et découvrir exactement quelle taille ils sont pour votre configuration et votre architecture.
Pour cette raison, j'essaie de m'en tenir à des types de données comme uint16_t
Lorsque j'ai besoin d'un type de données d'une longueur de bit particulière.
Edit: Désolé, j'ai initialement mal lu votre question.
La spécification C indique qu'un objet char
est "suffisamment grand pour stocker n'importe quel membre du jeu de caractères d'exécution". limits.h
Répertorie une taille minimale de 8 bits, mais la définition laisse la taille maximale d'un char
ouverte.
Ainsi, a char
est au moins aussi long que le plus grand caractère de l'ensemble d'exécution de votre architecture (généralement arrondi à la limite de 8 bits la plus proche). Si votre architecture a des opcodes plus longs, votre taille char
peut être plus longue.
Historiquement, l'opcode de la plate-forme x86 faisait un octet de long, donc char
était initialement une valeur de 8 bits. Les plates-formes x86 actuelles prennent en charge les opcodes de plus d'un octet, mais le char
est conservé à 8 bits car c'est à cela que les programmeurs (et les gros volumes de code x86 existant) sont conditionnés.
Lorsque vous pensez à la prise en charge multi-plateforme, profitez des types définis dans stdint.h
. Si vous utilisez (par exemple) un uint16_t, vous pouvez être sûr que cette valeur est une valeur 16 bits non signée sur n'importe quelle architecture, que cette valeur 16 bits corresponde à un char
, short
, int
, ou autre chose. La plupart du travail acharné a déjà été effectué par les personnes qui ont écrit votre compilateur/bibliothèques standard.
Si vous devez connaître la taille exacte d'un char
parce que vous effectuez une manipulation matérielle de bas niveau qui l'exige, j'utilise généralement un type de données suffisamment grand pour contenir un char
sur toutes les plates-formes prises en charge (généralement 16 bits suffisent) et exécutez la valeur via une routine convert_to_machine_char
lorsque j'ai besoin de la représentation exacte de la machine. De cette façon, le code spécifique à la plate-forme est limité à la fonction d'interface et la plupart du temps, je peux utiliser un uint16_t
Normal.
quel genre de considération vaut-il la peine de donner aux plates-formes avec un caractère non 8 bits?
les nombres magiques se produisent par exemple lors du déplacement;
la plupart de ceux-ci peuvent être traités tout simplement en utilisant CHAR_BIT et par exemple UCHAR_MAX au lieu de 8 et 255 (ou similaire).
j'espère que votre implémentation les définit :)
ce sont les problèmes "communs" .....
un autre problème indirect est que vous avez:
struct xyz {
uchar baz;
uchar blah;
uchar buzz;
}
cela peut "seulement" prendre (dans le meilleur des cas) 24 bits sur une plate-forme, mais peut prendre par exemple 72 bits ailleurs .....
si chaque uchar contenait des "indicateurs de bit" et que chaque uchar n'avait que 2 bits ou indicateurs "significatifs" que vous utilisiez actuellement, et que vous les avez organisés en 3 uchars pour plus de "clarté", alors cela pourrait être relativement "plus inutile", par exemple sur une plate-forme avec des uchars 24 bits .....
rien ne peut résoudre les champs de bits, mais ils ont d'autres choses à surveiller ...
dans ce cas, une seule énumération peut être un moyen d'obtenir le plus petit entier dont vous avez réellement besoin ...
peut-être pas un vrai exemple, mais des trucs comme ce "bit" me lors du portage/lecture avec du code ...
juste le fait que si un uchar est trois fois plus gros que ce qui est "normalement" attendu, 100 de ces structures pourraient gaspiller beaucoup de mémoire sur certaines plates-formes ..... où "normalement" ce n'est pas un gros problème .... .
donc les choses peuvent toujours être "cassées" ou dans ce cas "gaspiller beaucoup de mémoire très rapidement" en raison de l'hypothèse qu'un uchar n'est "pas très gaspilleur" sur une plate-forme, par rapport à RAM disponible, que sur une autre plateforme .....
le problème pourrait être plus important, par exemple pour les ints également, ou d'autres types, par ex. vous avez une structure qui nécessite 15 bits, donc vous la collez dans un int, mais sur une autre plate-forme, un int est de 48 bits ou autre .....
"normalement" vous pouvez le diviser en 2 uchars, mais par ex. avec un uchar 24 bits, vous n'en aurez besoin que d'un .....
donc une énumération pourrait être une meilleure solution "générique" ....
dépend de la façon dont vous accédez à ces bits :)
donc, il pourrait y avoir des "défauts de conception" qui éveillent leur tête ... même si le code peut toujours fonctionner/fonctionner correctement quelle que soit la taille d'un uchar ou uint ...
il y a des choses comme ça à surveiller, même s'il n'y a pas de "nombres magiques" dans votre code ...
j'espère que cela a du sens :)
les entiers étaient 16 bits (pdp11, etc.). Passer aux architectures 32 bits a été difficile. Les gens s'améliorent: presque personne ne suppose qu'un pointeur s'intégrera plus longtemps (vous n'avez pas raison?). Ou des décalages de fichiers, ou des horodatages, ou ...
Les caractères 8 bits sont déjà un peu anachroniques. Nous avons déjà besoin de 32 bits pour contenir tous les jeux de caractères du monde.