Comportement déroutant de sizeof avec des caractères

#include  #include  int main(void) { char ch='a'; printf("sizeof(ch) = %d\n", sizeof(ch)); printf("sizeof('a') = %d\n", sizeof('a')); printf("sizeof('a'+'b'+'C') = %d\n", sizeof('a'+'b'+'C')); printf("sizeof(\"a\") = %d\n", sizeof("a")); } 

Ce programme utilise sizeof pour calculer les tailles. Pourquoi la taille de 'a' différente de la taille de ch (où ch='a' )?

 sizeof(ch) = 1 sizeof('a') = 4 sizeof('a'+'b'+'C') = 4 sizeof("a") = 2 

TL; DRsizeof fonctionne sur le type de l’opérande.

  • sizeof(ch) == sizeof (char) ——————- (1)
  • sizeof('a') == sizeof(int) ——————– (2)
  • sizeof ('a'+ 'b' + 'c') == sizeof(int) — (3)
  • sizeof ("a") == sizeof (char [2]) ———- (4)

Voyons chaque cas maintenant.

  1. ch est défini comme étant de type char , donc assez simple.

  2. Dans C, sizeof('a') est identique à sizeof (int) , car une constante de caractère a un type entier.

    Citant C11 ,

    Une constante de caractère entier a le type int . […]

    En C ++, un littéral de caractère a le type char .

  3. sizeof est un opérateur à la compilation (sauf si l’opérande est un VLA), le type de l’expression est donc utilisé. Comme précédemment, toutes les constantes de type entier sont de type int , donc int + int + int produit int . Donc, le type de l’opérande est pris comme int .

  4. "a" est un tableau de deux caractères s, 'a' et 0 (terminateur NULL) ( non, il ne se désintègre pas pour pointer sur le premier élément du type tableau ), donc la taille est la même que celle d’un tableau avec deux éléments de caractère.


Cela dit, enfin, sizeof produit un résultat de type size_t , vous devez donc utiliser le spécificateur de format %zu pour imprimer le résultat.

Dans C, 'a' est une constante de type int . Ce n’est pas un char . Donc sizeof('a') sera identique à sizeof(int) .

sizeof(ch) est identique à sizeof(char) . (Le standard C garantit que toutes les constantes alphanumériques – et quelques autres – de la forme 'a' peuvent s’insérer dans un caractère, donc char ch='a'; est toujours bien défini.)

Notez qu’en C ++, 'a' est un littéral de type char ; encore une autre différence entre C et C ++.

Dans C, sizeof("a") est sizeof(char[2]) qui est 2. sizeof n’engendre pas la décroissance d’un type tableau vers un pointeur.

En C ++, sizeof("a") est sizeof(const char[2]) qui est 2. sizeof pas le déclin d’un type de tableau en un pointeur.

Dans les deux langues, 'a'+'b'+'C' est un type int dû, en C ++, à la promotion implicite de types intégraux.

Tout d’abord, le résultat de sizeof est de type size_t , qui doit être imprimé avec le spécificateur de format %zu . En ignorant cette partie et en supposant que int soit 4 octets, alors

  • printf("sizeof(ch) %d\n",sizeof(ch)); imprimera 1 en C et 1 en C ++.

    Cela est dû au fait qu’un caractère est garanti par définition dans les deux langues.

  • printf("sizeof('a') %d\n",sizeof('a')); imprimera 4 en C et 1 en C ++.

    C’est parce que les littéraux de caractères sont de type int en C, pour des raisons historiques 1) , mais ils sont de type char en C ++, car c’est ce que dicte le sens commun (et ISO 14882).

  • printf("sizeof('a'+'b'+'C) %d\n",sizeof('a'+'b'+'C')); imprimera 4 dans les deux langues.

    En C, le type résultant de int + int + int est naturellement int . En C ++, nous avons char + char + char . Mais le + invoque des règles implicites de promotion de type, donc on se retrouve avec int à la fin, peu importe.

  • printf("sizeof(\"a\") %d\n",sizeof("a")); imprimera 2 dans les deux langues.

    La chaîne littérale "a" est de type char[] en C et const char[] en C ++. Dans les deux cas, nous avons un tableau composé d’un terminateur a et d’un caractère nul: deux caractères.

    Comme remarque secondaire, cela se produit parce que le tableau "a" ne se désintègre pas en un pointeur vers le premier élément lorsqu’il est opérande à sizeof . Si nous provoquons un déclin de tableau en écrivant par exemple sizeof("a"+0) , nous aurons alors la taille d’un pointeur (probablement 4 ou 8).


1) Quelque part dans les âges sombres, il n’y avait pas de types et tout ce que vous écriviez se résumerait à peu importe. Puis, lorsque Dennis Ritchie a commencé à cuisiner ensemble des standards de facto pour C, il a apparemment décidé que les littéraux de caractère devraient toujours être promus en int . Et puis plus tard, quand C a été standardisé, ils ont dit que les littéraux de caractère sont simplement int .

Lors de la création de C ++, Bjarne Stroustrup a reconnu que tout cela n’avait pas beaucoup de sens et que les littéraux de caractères étaient de type char . Mais le comité C refuse obstinément de corriger ce défaut de langage.

Comme d’autres l’ont mentionné, le standard du langage C définit le type d’une constante de caractère à être int . La raison historique de ceci est que C, et son prédécesseur B, ont été initialement développés sur des mini-ordinateurs à PDP DEC avec différentes tailles de mots, qui supportaient l’ASCII 8 bits mais ne pouvaient effectuer que des calculs arithmétiques. Les premières versions de C définies int pour être la taille de mot native de la machine, et toute valeur inférieure à un int doit être élargie à int pour être transmise à une fonction ou à partir de celle-ci, ou utilisée dans une expression logique ou arithmétique , car c’était ainsi que fonctionnait le matériel sous-jacent.

C’est également pour cette raison que les règles de promotion des nombres entiers indiquent toujours que tout type de données plus petit qu’un int est promu dans int . Les implémentations en C sont également autorisées à utiliser un complément à deux au lieu du complément à deux pour des raisons historiques similaires, et le fait que le caractère échappe aux constantes octales et octales par défaut commence par 0 et les besoins hexadécimaux \x ou 0x avait des tailles de mot divisibles en morceaux de trois octets, mais pas de grignotages à quatre octets.

La promotion automatique sur int ne cause que des problèmes aujourd’hui. (Combien de programmeurs sont conscients que multiplier deux expressions uint32_t ensemble est un comportement indéfini, car certaines implémentations définissent int comme 64 bits de large, le langage exige que tout type de rang inférieur à int doit être promu en un int signé , multipliant deux int multiplicands a type int , la multiplication peut déborder d’un produit 64 bits signé, et c’est un comportement indéfini?) Mais c’est la raison pour laquelle C et C ++ sont coincés avec elle.

Je suppose que le code a été compilé en C.
Dans C, 'a' est traité comme un type int et int a une taille de 4. Dans C ++, 'a' est traité comme un type de caractère et si vous essayez de comstackr votre code dans cpp.sh, il doit renvoyer 1.