Hello,
Il s'agit en fait de tailles minimum garanties : Le C t'assure que tu pourras représenter de manière sûre, avec chaque type, une plage de nombres définies. Le C est un langage conçu pour s'adapter à tous les types de plateforme possibles, en tout cas la plupart et certaines sont très exotiques.
Il se trouve que les tailles spécifiées par la norme dans limits.h (voir C99 §5.2.4.2) correspondent correspondent effectivement aux formats les plus courants et qu'on retrouve également dans les autres langages, mêmes anciens, ce qui est preuve de bon sens. Mais il est tout-à-fait possible que les types exacts ne soient pas disponibles sur la plateforme pour laquelle tu compiles. C'est vrai pour la largeur des nombres mais aussi pour certaines subtilités arithmétiques comme la représentation en complément à 1 (qui nous avait occupés ici). Et dans ce cas, le programme compilé est bien obligé de travailler sur le plus proche format par excès.
Si tu fixes la taille des nombres et donc, en particulier, leur comportement lors des débordements, il est possible que le C ne puisse absolument pas compiler sur certains produits. C'est pourquoi la norme définit en parallèle stdint.h qui, lui, établit la correspondance entre les types natifs et les formats d'entiers à nombre de bits fixe.
Dans le premier cas, tu es assuré de pouvoir utiliser les types natifs dans tous tes programmes et qu'ils seront valides sur les plages considérées. Dans le second, tu es sûr que les entiers auront le format souhaité s'ils sont disponibles, mais rien ne te garantit qu'il le soient.
C'est aussi pour cela que la norme définit un grand nombre de cas « indéfinis » ou « spécifiques à l'implémentation ». Ce n'est pas le signe d'un document mal finalisé mais, au contraire, d'une notion importante qu'il faut savoir utiliser partout où c'est judicieux et surtout pas ailleurs. Imposer un comportement par défaut sans justification valable ferme de facto à toutes les classes d'architectures qui ne suivent pas le courant principal, ou leur impose des contraintes coûteuses et inutiles. C'est souvent le cas en électronique numérique, et spécialement en VHDL : si on a besoin d'un signal valide pour certaines conditions mais dont la valeur n'a pas de sens lorsqu'elles ne sont pas réunies, forcer ce signal à une valeur par défaut comme on initialiserait une variable en programmation séquentielle peut s'avérer extrêmement gourmand en logique combinatoire. Tous ceux qui ont eu un jour à programmer un FPGA se sont retrouvés confrontés à ce problème en dépassant sa capacité et ont dû faire des compromis pour faire tenir le tout dedans.
Les nombres à virgules flottantes, maintenant, sont un poil différents parce qu'ils sont « artificiels ». Il faut donc choisir un format et s'y tenir. La norme la plus répandue à ce sujet est IEEE 754 et c'est celle qui est utilisée par la norme C. Outre le fait que ces nombres soient définis « en dehors » de C89/C99, il est nécessaire de fixer leur largeur à cause de l'aspect « composite » de leur format et parce que la largeur de l'exposant dépend aussi de celle de la mantisse.
C'est une erreur.Et je suis encore plus surpris quand tu dis que "Les normes C ne définissent pas de tailles exactes pour chaque type mais une taille minimale". Certes je n'ai pas la connaissance absolue, et certainement des lacunes mais pour moi, les types Byte, Short, Long, DLong (long long), Float (single) et Double ont des tailles précisément définies et fixes indépendamment de tous langages. …
Déjà, en soi, « byte » ne signifie pas « octet », contrairement à une idée répandue, mais plutôt quelque chose comme « boîte à bits ». Ensuite « short », « long » « dlong », etc. ne sont justement pas définis au niveau global, mais uniquement par les normes respectives des différents langages à leur propre niveau, et sont nés à la fois de l'état de l'art à l'époque de leur apparition et de l'historique qui menait jusqu'à cette époque.
Par exemple, « word » signifie « mot ». On parle de « mot » à partir du moment où on fait une composition lexicale insécable en elle-même mais formée de plusieurs atomes, qui correspondent aux caractères en théorie des langages. On a utilisé le terme « mot » dans des contextes assez différents notamment quand on travaillait au niveau du bit.
Et en particulier, on a longtemps travaillé sur huit bits, au point qu'encore aujourd'hui, c'est l'unité d'adressage native des x86 qui travaillent pourtant directement sur 32, voire 64 bits aujourd'hui. Quand on programmait en assembleur sur ce genre de machine, on travaillait donc au niveau de l'octet sur toutes les opérations arithmétiques, donc le résultat avait le même format que les opérandes SAUF pour la multiplication dont la largeur du résultat est par nature le double de celui des opérandes (au maximum). Si on considère qu'à partir du moment où on travaillait sur un format composé de plus d'un octet, on avait forcément un mot et qu'avec ça, le bus de ces micro-processeur mesurait généralement 16 bits de large (parce qu'il est inconcevable d'éditer un micro-processeur généraliste avec un plan de mémoire de seulement 256 octets), alors la plupart des micro-processeurs des années 1980, comme le 6809 ou le Z80, étaient conçus autour d'une architecture 8 bits mais étaient en pratique des 8/16 bits puisqu'ils étaient faits pour pouvoir agréger deux accumulateurs 8 bits en un registre de 16 (A et B vers D sur 6809 et B-C, D-E, H-L… sur Z80) en plus d'être dotés de registres d'index nativement 16 bits (à commencer par le pointeur de programme), mais sur lesquels on ne pouvait souvent faire que des translations d'adresse, soit des additions ou soustractions.
Tout ceci a ancré l'association « mot = 16 bits » dans l'esprit collectif par opposition à l'octet puis, lorsque les architectures plus étendues ont commencé à se développer, on a inventé les « double mots », etc. toujours par comparaison avec les formats les précédant immédiatement.
Partager