De temps en temps, quelqu'un sur SO fait remarquer que char
(alias «octet») n'est pas nécessairement 8 bits .
Il semble que le 8 bits char
soit presque universel. J'aurais pensé que pour les plates-formes grand public, il est nécessaire d'avoir un 8 bits char
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 seraient-ils différents des 8 bits "normaux"?
Lors de l'écriture de code et de la réflexion sur 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 non 8 bits char
?
Dans le passé, j'ai rencontré des DSP Analog Devices pour char
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 eu beaucoup d'expérience avec C sur cette plate-forme.)
la source
Réponses:
char
est également 16 bits sur les DSP Texas Instruments C54x, qui sont par exemple montés dans OMAP2. Il existe d'autres DSP avec 16 et 32 bitschar
. Je pense que j'ai même entendu parler d'un DSP 24 bits, mais je ne me souviens pas quoi, alors peut-être que je l'ai imaginé.Une autre considération est que les mandats POSIX
CHAR_BIT == 8
. Donc, si vous utilisez POSIX, vous pouvez le supposer. Si quelqu'un a besoin plus tard de porter votre code vers une quasi-implémentation de POSIX, cela se trouve juste pour avoir les fonctions que vous utilisez mais une taille différentechar
, 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 penser. Tapez simplement
CHAR_BIT
. Si vous voulez un type 8 bits exact, utilisezint8_t
. Votre code échouera bruyamment à se compiler sur des implémentations qui n'en fournissent pas, au lieu d'utiliser silencieusement une taille à laquelle vous ne vous attendiez pas. À tout le moins, si je frappe un cas où j'avais une bonne raison de le supposer, alors je l'affirmerais.la source
assert()
(si c'est ce que vous vouliez dire), j'utiliserais#if CHAR_BIT != 8
...#error "I require CHAR_BIT == 8"
...#endif
static_assert()
?Ce n'est pas tant qu'il «vaut la peine de prendre en considération» quelque chose, car il respecte les règles. En C ++, par exemple, la norme dit 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 sans opération particulier prenant un nombre fixe de cycles nécessiterait une durée d'horloge fixe, car la plupart des processeurs grand public avaient à peu près la même puissance. Malheureusement, les ordinateurs sont devenus plus rapides très rapidement. Cela a engendré la montée en puissance des boîtes avec des boutons "Turbo" - dont le but, ironiquement, était de ralentir l'ordinateur afin que les jeux utilisant la technique de la temporisation puissent être joués à une vitesse raisonnable.
Un commentateur a demandé où dans la norme il était dit 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. Elle dit également:Ainsi, tout nombre égal ou supérieur à 8 peut être remplacé par une implémentation dans
CHAR_BIT
.la source
char
il y en a plus de 64 mais moins de 128 donc 7 bits suffiraient.Les machines avec des architectures 36 bits ont des octets de 9 bits. Selon Wikipedia, les machines avec des architectures 36 bits comprennent:
la source
Quelques-uns dont je suis au courant:
la source
char
type? Je sais que les bibliothèques système ne prenaient en charge que les versions à caractères larges des fonctions qui acceptent des chaînes, et qu'au moins certaines versions de WinCE ont supprimé les fonctions de chaîne ANSI telles que strlen, pour vous empêcher de gérer la chaîne de caractères. Mais n'avait-il vraiment pas du tout de type char? C'était quoisizeof(TCHAR)
? Quel type est retourné malloc? Comment lebyte
type Java a-t-il été implémenté?Il n'existe pas de code totalement 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
etUCHAR_MAX
. Oui, il est parfois possible d'écrire du code qui ne dépend pas de la taille du caractère.Cependant, presque tous les codes réels ne sont pas autonomes. 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. Ensuite, vous devez le sérialiser. La simple copie binaire d'une structure dans un tampon de sortie n'est pas portable: généralement, vous ne connaissez ni l'ordre des octets de la plate-forme, ni l'alignement des membres de la structure, donc 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)memcpy
dans le tampon. Pourquoimemcpy
? Parce qu'il existe de nombreuses plates-formes sur lesquelles il n'est pas possible d'écrire en 32 bits (16 bits, 64 bits - aucune différence) lorsque l'adresse cible n'est pas correctement alignée.Donc, vous avez 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 le tampon doit-il être? Si vos caractères sont 9 bits? S'ils sont 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 emballé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 convienne à tous les cas. Beaucoup de choses dépendent de l'implémentation du socket pour la plate-forme cible.
Alors, de quoi je parle. Habituellement, le code peut être relativement facilement rendu 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 demande beaucoup d'efforts et donne souvent peu , 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% du code, la capacité de fonctionner 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 d'octet et effectuez une assertion de temps de compilation. Vous devrez presque sûrement réécrire beaucoup pour une plate-forme très inhabituelle.
Mais si votre code est hautement "autonome" - pourquoi pas? Vous pouvez l'écrire d'une manière qui autorise différentes tailles d'octets.
la source
unsigned char
valeur, il ne devrait y avoir aucun problème de portabilité à moins que le code n'utilise des astuces d'alias plutôt que des décalages pour convertir des séquences d'octets vers / à partir de types entiers plus grands. Personnellement, je pense que la norme C devrait définir des éléments intrinsèques pour emballer / décompresser des entiers à partir de séquences de types plus courts (le plus généralementchar
) stockant un nombre fixe de bits disponibles garantis par élément (8 parunsigned char
, 16 parunsigned short
ou 32 parunsigned long
).Il semble que vous puissiez toujours acheter un IM6100 (c'est-à-dire un PDP-8 sur puce) dans un entrepôt. C'est une architecture 12 bits.
la source
De nombreuses puces DSP ont 16 ou 32 bits
char
. TI fabrique régulièrement de telles puces par exemple .la source
Extrait de http://en.wikipedia.org/wiki/Byte#History
Pas sûr des autres langues cependant.
http://en.wikipedia.org/wiki/IBM_7030_Stretch#Data_Formats
Définit un octet sur cette machine comme étant de longueur variable
la source
La famille DEC PDP-8 avait un mot de 12 bits bien que vous utilisiez habituellement 8 bits ASCII pour la sortie (sur un télétype principalement). Cependant, il y avait aussi un code de caractères à 6 bits qui vous permettait d'encoder 2 caractères dans un seul mot de 12 bits.
la source
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. Utilisation
sizeof
et les valeurs danslimits.h
si vous souhaitez interroger vos types de données et découvrir exactement leur taille pour votre configuration et votre architecture.Pour cette raison, j'essaie de m'en tenir aux types de données, comme
uint16_t
lorsque j'ai besoin d'un type de données d'une longueur de bits particulière.Edit: Désolé, j'ai initialement mal lu votre question.
La spécification C indique qu'un
char
objet 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'unchar
ouvert.Ainsi, le a
char
est au moins aussi long que le caractère le plus grand 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, votrechar
taille 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 lachar
longueur est de 8 bits car c'est à cela que les programmeurs (et les grands volumes de code x86 existant) sont conditionnés.Lorsque vous pensez à la prise en charge multiplateforme, tirez parti des types définis dans
stdint.h
. Si vous utilisez (par exemple) un uint16_t, alors vous pouvez être sûr que cette valeur est une valeur non signée 16 bits sur l' architecture tout, que ce soit qui correspond à valeur 16 bits à unchar
,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 avez besoin de connaître la taille exacte d'un
char
parce que vous effectuez une manipulation matérielle de bas niveau qui le nécessite, j'utilise généralement un type de données suffisamment grand pour contenir unchar
sur toutes les plates-formes prises en charge (généralement 16 bits suffisent) et j'exécute la valeur via uneconvert_to_machine_char
routine 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 fichier normaluint16_t
.la source
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 "courants" .....
un autre problème indirect est de dire que vous avez:
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 "bit flags" et que chaque uchar n'avait que 2 bits ou flags "significatifs" que vous utilisiez actuellement, et que vous les organisiez seulement en 3 uchars pour "clarté", alors cela pourrait être relativement "plus gaspilleur" par exemple sur une plate-forme avec uchars 24 bits .....
rien que les champs de bits ne peuvent résoudre, 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 ça me "mord" quand on porte / joue avec du code .....
juste le fait que si un uchar est trois fois plus grand que ce qui est "normalement" attendu, 100 de ces structures pourraient gaspiller beaucoup de mémoire sur certaines plates-formes ..... là où "normalement" ce n'est pas un gros problème .... .
donc les choses peuvent encore ê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 à la RAM disponible, que sur une autre plate-forme ... ..
le problème peut être plus important, par exemple pour les ints également, ou pour d'autres types, par exemple, vous avez une structure qui a besoin de 15 bits, vous la collez donc dans un int, mais sur une autre plate-forme, un int est de 48 bits ou autre ... .
"normalement" vous pourriez le diviser en 2 uchars, mais par exemple avec un uchar 24 bits, vous n'en auriez 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 peut y avoir des "défauts de conception" qui leur viennent à l'esprit .... même si le code peut toujours fonctionner / fonctionner correctement quelle que soit la taille d'un uchar ou d'un 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 :)
la source
enum
est susceptible d'être plus petit que les autres types natifs? Savez-vous qu'il utilise par défaut le même stockage queint
? « vous avez une structure qui a besoin 15 bits, de sorte que vous tenez dans un int, mais sur une autre plate - forme est un entier 48 bits ou quoi que ..... » - donc#include <cstdint>
et en faire unint16_t
pour la meilleure chance de minimiser l' utilisation de bits . Je ne sais vraiment pas ce que vous pensiez dire parmi toutes ces ellipses.Les ints étaient 16 bits (pdp11, etc.). Passer à des architectures 32 bits était difficile. Les gens vont mieux: presque personne ne suppose qu'un pointeur tiendra 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.
la source
char
est un peu étrange maintenant à l'époque Unicode. Je me soucie plus des unités 8 bits (octets) lorsque je traite des données binaires, par exemple le stockage de fichiers, les communications réseau.uint8_t
est plus utile.