web-dev-qa-db-fra.com

Comportement déroutant de sizeof avec des caractères

#include <stdio.h>
#include <string.h>

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' est-elle différente de celle de ch (où ch='a')?

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

TL; DR - sizeof 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. En C, sizeof('a') est identique à sizeof (int), dans la mesure où une constante de caractère a un type entier.

    Citer C11,

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

    En C++, un caractère literal a pour type char.

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

  4. "a" est un tableau de deux chars, 'a' et 0 (terminateur nul) (non, il ne se désactive pas en pointeur vers le premier élément du type de tableau). deux éléments char.


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

51
Sourav Ghosh

En C, 'a' est constant de type int. C'est pas a char. Donc, sizeof('a') sera identique à sizeof(int).

sizeof(ch) est identique à sizeof(char). (La norme C garantit que toutes les constantes alphanumériques - et quelques autres - de la forme 'a' peuvent tenir dans une char, de sorte que char ch='a'; est toujours bien défini.)

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

En C, sizeof("a") est sizeof(char[2]) qui est 2. sizeof n'entraîne pas le decay d'un type de tableau en pointeur.

En C++, sizeof("a") est sizeof(const char[2]) qui est 2. sizeof n'entraîne pas le decay d'un type de tableau en pointeur.

Dans les deux langages, 'a'+'b'+'C' est un type int en raison, en C++, de la promotion implicite des types intégraux.

23
Bathsheba

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 vaut 4 octets, alors

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

    En effet, il est garanti que char est d'un octet dans les deux langues.

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

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

  • 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 les règles de promotion de types implicites nous aboutissons donc à int à la fin peu importe.

  • printf("sizeof(\"a\") %d\n",sizeof("a")); en 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'une a et d'un terminateur nul: deux caractères.

    En remarque, cela se produit car le tableau "a" ne se décompose pas en un pointeur sur le premier élément lorsque l'opérande à sizeof. Si nous provoquions une dégradation d'un tableau en écrivant par exemple sizeof("a"+0), nous aurions plutôt la taille d'un pointeur (probablement 4 ou 8).


1) Quelque part au cours de l’âge sombre, il n’y avait pas de types et tout ce que vous avez écrit se résumerait à int peu importe. Puis, quand Dennis Ritchie a commencé à cuisiner ensemble une sorte de standard de facto pour C, il a apparemment décidé que les littéraux de caractère devraient toujours être promu à int. Et plus tard, quand C a été normalisé, ils ont dit que les littéraux de caractères 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ère devaient taper char comme ils devraient l’être. Mais le comité C refuse obstinément de corriger ce défaut de langage.

9
Lundin

Comme d'autres l'ont mentionné, la norme de langage C définit le type d'une constante de caractère comme étant int. La raison historique en est que C et son prédécesseur B ont été développés à l’origine sur des mini-ordinateurs DEC PDP avec différentes tailles de Word, qui prenaient en charge le format ASCII à 8 bits, mais ne pouvaient effectuer des opérations arithmétiques que sur les registres. Les premières versions de C définissaient int comme étant la taille de mot native de la machine et toute valeur inférieure à int devait être élargie à int pour pouvoir être transmise à ou depuis une fonction ou utilisée dans une expression binaire, logique ou arithmétique , c’est ainsi que fonctionnait le matériel sous-jacent.

C’est aussi pourquoi les règles de promotion des entiers indiquent toujours que tout type de données inférieur à int est promu à int. Les implémentations C sont également autorisées à utiliser les mathématiques complément à complément au lieu de complément à deux pour des raisons historiques similaires, et le fait que le caractère échappe par défaut aux constantes octales et octales commence par juste 0 et que hex requiert \x ou 0x, ce qui était le cas pour les mini-ordinateurs du début de DEC. La taille des mots est divisible en morceaux de trois octets, mais pas en quatre octets.

La promotion automatique à int ne cause que des problèmes aujourd'hui. (Combien de programmeurs savent que multiplier deux expressions uint32_t constitue un comportement indéfini, car certaines implémentations définissent int sur une largeur de 64 bits, le langage requiert que tout type de rang inférieur à int soit promu en signed int, le résultat de la multiplication de deux multiplicandes int a le type int, la multiplication peut déborder d'un produit 64 bits signé, ce qui est un comportement indéfini?) Mais c'est la raison pour laquelle C et C++ sont coincés avec lui.

2
Davislor

Je suppose que le code a été compilé en C.
En C, 'a' est traité comme un type int et int a une taille de 4 . En C++, 'a' est traité comme un type char et si vous essayez de compiler votre code dans cpp.sh, il devrait renvoyer 1.

0
Wolf