45 votes

Comportement confus de sizeof avec les 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' différente de la taille de ch (où ch='a' ) ?

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

6 votes

Vous devriez utiliser %zu comme sizeof renvoie à size_t pas int

24 votes

Vous devez marquer ce code en C ou C++, car il donnera des réponses très différentes selon le langage. En fait, le C++ a reconnu que le C était stupide et a corrigé plusieurs défauts évidents du langage, alors que le C refuse d'admettre qu'il est stupide.

1 votes

@Sam Varshavchik Pas nécessairement un double car les deux premières lignes donneront 1 contre 4 en C, mais 1 contre 1 en C++. La troisième ligne va en effet s'amuser avec la promotion implicite en C++, mais pas en C.

52voto

Sourav Ghosh Points 54713

TL;DR - sizeof travaille 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 maintenant chaque cas.

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

  2. En C, sizeof('a') est la même chose que sizeof (int) comme une constante de caractère de type entier.

    Citation : C11 ,

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

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

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

  4. "a" est un tableau de deux char s, 'a' y 0 (terminateur nul) ( non, il ne se désintéresse pas du pointeur vers le premier élément du type tableau ), donc la taille est la même que celle d'un tableau avec deux char éléments.


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

0 votes

Je me demande quels problèmes, le cas échéant, auraient résulté du fait de faire sizeof opérer uniquement sur les valeurs l ? En particulier sur les systèmes qui utilisent FLT_EVAL_METHOD==2 et qui ont une long double qui est plus grand que double il serait un peu bizarre de suggérer que sizeof (1.0/10.0) doit signaler 8 même si long double d = (1.0/10.0); stockerait une valeur qui ne peut pas être représentée dans un "double" de 8 octets.

0 votes

Suggérer "la constante de caractère a le type integer" --> "la constante de caractère a le type int ".

1 votes

@supercat Entre autres choses, elle aurait probablement rendu illégale la construction suivante : sizeof(type) .

23voto

Bathsheba Points 23209

En C, 'a' es constant de type int . Il est no a char . Donc sizeof('a') sera le même que sizeof(int) .

sizeof(ch) est la même chose que sizeof(char) . (La norme C garantit que toutes les constantes alphanumériques - et quelques autres - de la forme 'a' peut tenir dans un char 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++.

En C, sizeof("a") es sizeof(char[2]) qui est de 2. sizeof n'est pas à l'origine de la décroissance d'un type de tableau à un pointeur.

En C++, sizeof("a") es sizeof(const char[2]) qui est de 2. sizeof n'est pas à l'origine de la décroissance d'un type de tableau à un pointeur.

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

2 votes

Excellente réponse, à l'exception du problème mineur de ne pas être explicite sur 'a'+'b'+'C' étant un exemple de promotion intégrale pas conversion intégrale en termes standard. (Les deux sont conversions car ce terme est également utilisé comme terme générique. La dénomination est intéressante).

1 votes

@ArneVogel : Merci, si j'avais un dollar à chaque fois que je dis ou écris cela incorrectement....

1 votes

@chux Merci, j'ai corrigé mais je pense que je vais laisser tous les trucs C++ en place - les joies d'une question qui bouge !

9voto

Lundin Points 21616

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

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

    En effet, un char est par définition garantie à 1 octet 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 le bon sens (et la norme ISO 14882) impose.

  • 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++, on a char + char + char . Mais le + invoque 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 disposons d'un tableau composé de a et un terminateur nul : deux caractères.

    À titre d'information, cela se produit parce que le tableau "a" ne se décompose pas en un pointeur vers le premier élément lorsque l'opérande à sizeof . Devrions-nous provoquer une désintégration du tableau en écrivant par exemple sizeof("a"+0) alors nous obtiendrions la taille d'un pointeur à la place (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ésumait à int aucune importance. Puis, lorsque Dennis Ritchie a commencé à préparer une sorte de norme de facto pour le C, il a apparemment décidé que les littéraux de caractères devaient toujours être promu a int . Et plus tard, quand C a été standardisé, ils ont dit que les littéraux de caractères sont simplement int .

Lors de la création du C++, Bjarne Stroustrup a reconnu que tout cela n'avait pas beaucoup de sens et a fait des littéraux des caractères de type char comme ils devraient l'être. Mais le comité C refuse obstinément de corriger ce défaut de langage.

0 votes

Mes copies de la norme C89 et C99 définissent sizeof pour renvoyer les comptes des "unités de stockage", et non des "octets", quels qu'ils soient.

0 votes

@EricTowers "byte" est aujourd'hui typiquement utilisé pour 8 bits, mais sizeof renvoie le nombre de char s - et un C char peut être supérieure à 8 bits (elle est de 16 bits sur un CPU avec lequel je travaille, par exemple).

0 votes

@pipe : Et un octet a été de 9 bits sur les architectures sur lesquelles j'ai travaillé. Mon point de vue est que, puisque la norme ne définit pas ou n'utilise pas les "octets", il est incorrect d'avoir "est par définition garanti d'être 1 octet".

2voto

Lorehead Points 953

Comme d'autres l'ont mentionné, la norme du langage C définit le type d'une constante de caractère comme étant int . La raison historique 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 mots, qui supportaient l'ASCII 8 bits mais ne pouvaient effectuer des opérations arithmétiques que sur des registres. Les premières versions de C définissaient int pour être la taille de mot native de la machine, et toute valeur inférieure à un int devait être élargi à int afin d'être transmis à une fonction ou à partir d'une fonction, ou utilisé dans une expression logique, arithmétique ou par bit, car c'est ainsi que le matériel sous-jacent fonctionne.

C'est également la raison pour laquelle les règles de promotion des entiers stipulent toujours que tout type de données plus petit qu'un int est promu à int . Les implémentations C sont également autorisées à utiliser les mathématiques à complément à un au lieu de celles à complément à deux pour des raisons historiques similaires, et le fait que les échappements de caractères sont par défaut en octal et que les constantes octales commencent par juste 0 et les besoins hexagonaux \x o 0x est que les premiers mini-ordinateurs DEC avaient des tailles de mots divisibles en morceaux de trois octets, mais pas en morceaux de quatre octets.

Promotion automatique vers int ne cause que des problèmes aujourd'hui. (Combien de programmeurs sont conscients que multiplier deux uint32_t ensemble est un comportement non défini, parce que certaines implémentations définissent int de 64 bits de large, le langage exige que tout type de rang inférieur à int doit être promu à un signé int le résultat de la multiplication de deux int multiplicandes a le type int la multiplication peut déborder un produit signé de 64 bits, ce qui constitue un comportement indéfini). Mais c'est la raison pour laquelle C et C++ sont coincés avec cela.

0 votes

Thanx..Bonne recherche cependant

0 votes

Notez que les auteurs de la norme ont expressément reconnu la possibilité qu'une implémentation soit conforme tout en étant d'une qualité si médiocre qu'elle est inutile, mais ils supposent que les implémentations de qualité ne feront pas d'efforts pour se comporter de la manière la moins utile que la norme permettrait. Les justifications de toutes les versions de la norme décrivent des expressions pour lesquelles elles s'attendent à ce que des implémentations courantes de qualité traitent les mathématiques signées et non signées de manière identique. L'UB résultant des promotions non désirées vers les signes ne sera un problème que lors de l'utilisation de compilateurs de faible qualité...

0 votes

...(ce que, pour une raison quelconque, les programmeurs sont devenus trop enclins à tolérer). Le fait qu'un morceau de code ne fonctionne pas sur un compilateur conçu pour être de qualité inutilement mauvaise ne signifie pas que le code est cassé. Il serait impossible d'écrire un programme qui ne pourrait pas être coulé par une "implémentation conforme" de qualité suffisamment mauvaise.

0voto

Wolf Points 89

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

6 votes

" int a une taille de 4 " Habituellement, oui. Mais pas toujours.

1 votes

J'ai une plateforme où sizeof(int)==1 .

1 votes

@pipe Avec quelle plateforme/compilateur est-ce que c'est le cas ? sizeof(int)==1 ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X