En C ++, sizeof('a') == sizeof(char) == 1
. Cela a un sens intuitif, car il 'a'
s'agit d'un caractère littéral et sizeof(char) == 1
tel que défini par la norme.
En C cependant sizeof('a') == sizeof(int)
. Autrement dit, il semble que les littéraux de caractères C soient en fait des entiers. Quelqu'un sait-il pourquoi? Je peux trouver beaucoup de mentions de cette bizarrerie C mais aucune explication pour pourquoi elle existe.
Réponses:
discussion sur le même sujet
la source
char
variable n'est pas un int, donc rendre une constante de caractère un est un cas particulier. Et il est facile d'utiliser une valeur de caractère sans promotion:c1 = c2;
. OTOH,c1 = 'x'
est une conversion à la baisse. Plus important encore,sizeof(char) != sizeof('x')
ce qui est un sérieux problème de langage. Quant aux constantes de caractères multi-octets: c'est la raison, mais elles sont obsolètes.La question initiale est "pourquoi?"
La raison en est que la définition d'un caractère littéral a évolué et changé, tout en essayant de rester rétrocompatible avec le code existant.
Dans les jours sombres du début du C, il n'y avait aucun type. Au moment où j'ai appris à programmer en C, les types avaient été introduits, mais les fonctions n'avaient pas de prototypes pour dire à l'appelant quels étaient les types d'arguments. Au lieu de cela, il était normalisé que tout ce qui était passé en paramètre aurait soit la taille d'un int (cela incluait tous les pointeurs), soit un double.
Cela signifiait que lorsque vous écriviez la fonction, tous les paramètres qui n'étaient pas doubles étaient stockés sur la pile en tant qu'ints, quelle que soit la manière dont vous les avez déclarés, et le compilateur a mis du code dans la fonction pour gérer cela pour vous.
Cela rendait les choses quelque peu incohérentes, alors quand K&R a écrit son célèbre livre, ils ont mis dans la règle qu'un littéral de caractère serait toujours promu en int dans n'importe quelle expression, pas seulement un paramètre de fonction.
Lorsque le comité ANSI a normalisé C pour la première fois, il a changé cette règle pour qu'un caractère littéral soit simplement un int, car cela semblait être un moyen plus simple de réaliser la même chose.
Lors de la conception de C ++, toutes les fonctions devaient avoir des prototypes complets (ce n'est toujours pas nécessaire en C, bien que cela soit universellement accepté comme une bonne pratique). Pour cette raison, il a été décidé qu'un littéral de caractère pouvait être stocké dans un char. L'avantage de ceci en C ++ est qu'une fonction avec un paramètre char et une fonction avec un paramètre int ont des signatures différentes. Cet avantage n'est pas le cas chez C.
C'est pourquoi ils sont différents. Évolution...
la source
void f(unsigned char)
Vsvoid f(signed char)
.f('a')
, vous voulez probablement que la résolution de surcharge choisissef(char)
pour cet appel plutôt quef(int)
. Les tailles relatives deint
etchar
ne sont pas pertinentes, comme vous le dites.Je ne connais pas les raisons spécifiques pour lesquelles un littéral de caractère en C est de type int. Mais en C ++, il y a une bonne raison de ne pas suivre cette voie. Considère ceci:
Vous vous attendez à ce que l'appel à imprimer sélectionne la deuxième version prenant un caractère. Avoir un caractère littéral étant un int rendrait cela impossible. Notez que dans les littéraux C ++ ayant plus d'un caractère ont toujours le type int, bien que leur valeur soit définie par l'implémentation. Donc,
'ab'
a typeint
, tandis que'a'
a typechar
.la source
en utilisant gcc sur mon MacBook, j'essaye:
qui lors de l'exécution donne:
ce qui suggère qu'un caractère est de 8 bits, comme vous le pensez, mais un littéral de caractère est un int.
la source
À l'époque où C était en cours d'écriture, le langage d'assemblage MACRO-11 du PDP-11 avait:
Ce genre de chose est assez courant dans le langage d'assemblage - les 8 bits faibles contiendront le code de caractère, les autres bits effacés à 0. PDP-11 avait même:
Cela a fourni un moyen pratique de charger deux caractères dans les octets bas et haut du registre 16 bits. Vous pouvez ensuite les écrire ailleurs, en mettant à jour certaines données textuelles ou la mémoire d'écran.
Ainsi, l'idée de caractères promus pour enregistrer la taille est tout à fait normale et souhaitable. Mais, disons que vous devez insérer 'A' dans un registre non pas dans le cadre de l'opcode codé en dur, mais depuis quelque part dans la mémoire principale contenant:
Si vous voulez lire juste un «A» de cette mémoire principale dans un registre, lequel liriez-vous?
Certains processeurs peuvent uniquement prendre en charge la lecture d'une valeur 16 bits dans un registre 16 bits, ce qui signifierait qu'une lecture à 20 ou 22 nécessiterait alors l'effacement des bits de `` X '', et en fonction de l'endianité du processeur, l'un ou l'autre aurait besoin de passer dans l'octet de poids faible.
Certains processeurs peuvent nécessiter une lecture alignée sur la mémoire, ce qui signifie que l'adresse la plus basse impliquée doit être un multiple de la taille des données: vous pourrez peut-être lire à partir des adresses 24 et 25, mais pas 27 et 28.
Ainsi, un compilateur générant du code pour obtenir un 'A' dans le registre peut préférer gaspiller un peu de mémoire supplémentaire et encoder la valeur comme 0 'A' ou 'A' 0 - en fonction de l'endianness, et en s'assurant également qu'il est correctement aligné ( c'est-à-dire pas à une adresse mémoire impaire).
Je suppose que C a simplement porté ce niveau de comportement centré sur le processeur, en pensant aux constantes de caractère occupant des tailles de registre de mémoire, confirmant l'évaluation courante de C comme un "assembleur de haut niveau".
(Voir 6.3.3 à la page 6-25 de http://www.dmv.net/dec/pdf/macro.pdf )
la source
Je me souviens avoir lu K&R et vu un extrait de code qui lirait un caractère à la fois jusqu'à ce qu'il atteigne EOF. Étant donné que tous les caractères sont des caractères valides dans un fichier / flux d'entrée, cela signifie que EOF ne peut pas être une valeur de caractère. Ce que le code a fait était de mettre le caractère lu dans un int, puis de tester EOF, puis de le convertir en caractère si ce n'était pas le cas.
Je me rends compte que cela ne répond pas exactement à votre question, mais il serait logique que le reste des littéraux de caractère soit sizeof (int) si le littéral EOF l'était.
la source
Je n'ai pas vu de justification à cela (les littéraux de caractères C étant des types int), mais voici quelque chose que Stroustrup avait à dire à ce sujet (de Design and Evolution 11.2.1 - Fine-Grain Resolution):
Donc, pour la plupart, cela ne devrait poser aucun problème.
la source
La raison historique en est que C, et son prédécesseur B, ont été développés à l'origine sur divers modèles de mini-ordinateurs DEC PDP avec différentes tailles de mots, qui supportaient l'ASCII 8 bits mais ne pouvaient exécuter l'arithmétique que sur les registres. (Pas le PDP-11, cependant; cela est venu plus tard.) Les premières versions de C
int
étaient définies comme étant la taille de mot native de la machine, et toute valeur inférieure à unint
devait être élargieint
pour être transmise vers ou depuis une fonction , ou utilisé dans une expression binaire, logique ou arithmétique, car c'était ainsi que fonctionnait le matériel sous-jacent.C'est aussi pourquoi les règles de promotion d'entiers indiquent toujours que tout type de données inférieur à an
int
est promu versint
. Les implémentations C sont également autorisées à utiliser les mathématiques du complément à un au lieu du complément à deux pour des raisons historiques similaires. La raison pour laquelle les échappements de caractères octaux et les constantes octales sont des citoyens de première classe par rapport à hexadécimal est également que ces premiers mini-ordinateurs DEC avaient des tailles de mot divisibles en blocs de trois octets mais pas en grignotages de quatre octets.la source
char
faisait exactement 3 chiffres octauxC'est le comportement correct, appelé «promotion intégrale». Cela peut également arriver dans d'autres cas (principalement des opérateurs binaires, si je me souviens bien).
EDIT: Juste pour être sûr, j'ai vérifié ma copie de Programmation Expert C: Deep Secrets , et j'ai confirmé qu'un char littéral ne commence pas par un type int . Il est initialement de type char mais lorsqu'il est utilisé dans une expression , il est promu en int . Ce qui suit est cité du livre:
la source
Je ne sais pas, mais je suppose que c'était plus facile de l'implémenter de cette façon et que cela n'avait pas vraiment d'importance. Ce n'est qu'en C ++ que le type a pu déterminer quelle fonction serait appelée qu'il fallait la corriger.
la source
Je ne savais pas cela en effet. Avant que les prototypes n'existent, tout ce qui est plus étroit qu'un int était converti en un int lors de son utilisation comme argument de fonction. Cela peut faire partie de l'explication.
la source
char
enint
rendrait tout à fait inutile que les constantes de caractère soient des entiers. Ce qui est pertinent, c'est que le langage traite les constantes de caractères différemment (en leur donnant un type différent) deschar
variables, et ce qu'il faut, c'est une explication de cette différence.Ce n'est que tangentiel à la spécification du langage, mais dans le matériel, le processeur n'a généralement qu'une seule taille de registre - 32 bits, disons - et donc chaque fois qu'il fonctionne réellement sur un caractère (en l'ajoutant, en le soustrayant ou en le comparant) il y a une conversion implicite en int lorsqu'il est chargé dans le registre. Le compilateur prend soin de bien masquer et de déplacer le nombre après chaque opération de sorte que si vous ajoutez, par exemple, 2 à (caractères non signés) 254, il s'enroulera à 0 au lieu de 256, mais à l'intérieur du silicium, c'est vraiment un int jusqu'à ce que vous le sauvegardiez en mémoire.
C'est en quelque sorte un point académique parce que le langage aurait pu spécifier un type littéral 8 bits de toute façon, mais dans ce cas, la spécification du langage reflète plus étroitement ce que fait vraiment le processeur.
(x86 wons peuvent noter qu'il existe par exemple une opération addh native qui ajoute les registres court-large en une seule étape, mais à l'intérieur du noyau RISC, cela se traduit en deux étapes: ajouter les nombres, puis étendre le signe, comme une paire add / extsh sur le PowerPC)
la source
char
variables ont des types différents. Les promotions automatiques, qui reflètent le matériel, ne sont pas pertinentes - elles sont en fait anti-pertinentes, car leschar
variables sont automatiquement promues, ce n'est donc pas une raison pour que les littéraux de caractères ne soient pas de typechar
. La vraie raison est les littéraux multi-octets, qui sont maintenant obsolètes.