Int8_t et uint8_t sont-ils destinés à être des types de caractères?

Compte tenu de ce programme C ++ 11, devrais-je m’attendre à voir un numéro ou une lettre? Ou ne pas faire d’attentes?

#include  #include  int main() { int8_t i = 65; std::cout << i; } 

La norme spécifie-t-elle si ce type peut ou sera un type de caractère?

D’après le § 18.4.1 [cstdint.syn] du C ++ 0x FDIS (N3290), int8_t est un typedef facultatif spécifié comme suit:

 namespace std { typedef signed integer type int8_t; // optional //... } // namespace std 

Le § 3.9.1 [fondamental] énonce:

Il existe cinq types entiers signés standard : « signed char », « short int », « int », « long int » et « long long int ». Dans cette liste, chaque type fournit au moins autant de stockage que ceux qui le précèdent dans la liste. Il peut également y avoir des types d’entiers signés étendus définis par l’implémentation. Les types d’entier signé standard et étendu sont collectivement appelés types d’entiers signés .

Les types bool , char , char16_t , char32_t , wchar_t et les types entiers signés et non signés sont collectivement appelés types intégraux . Un synonyme de type intégral est un type entier .

Le § 3.9.1 stipule également:

Dans toute implémentation particulière, un object char simple peut prendre les mêmes valeurs qu’un caractère signed char ou un caractère unsigned char ; lequel est défini par la mise en œuvre.

Il est tentant de conclure que int8_t peut être un typedef de caractères à condition que les objects char prennent des valeurs signées; Cependant, ce n’est pas le cas, car char ne fait pas partie de la liste des types entiers signés ( types d’entiers signés standard et éventuellement étendus). Voir aussi les commentaires de Stephan T. Lavavej sur std::make_unsigned et std::make_signed .

Par conséquent, soit int8_t est un typedef de caractères signed char soit un type entier signé étendu dont les objects occupent exactement 8 bits de stockage.

Pour répondre à votre question, cependant, vous ne devriez pas faire d’hypothèses. Parce que les fonctions des deux formes x.operator<<(y) et operator<<(x,y) ont été définies, le § 13.5.3 [over.binary] dit que nous nous référons au § 13.3.1.2 [over.match.oper ] pour déterminer l'interprétation de std::cout << i . Le § 13.3.1.2 dit à son tour que l'implémentation choisit dans l'ensemble des fonctions candidates conformément aux § 13.3.2 et 13.3.3. Nous examinons ensuite le § 13.3.3.2 [over.ics.rank] pour déterminer que:

  • Le template basic_ostream& operator<<(basic_ostream&, signed char) serait appelé si int8_t est une correspondance exacte pour un caractère signed char (ie un typedef de caractère signed char ).
  • Sinon, l' int8_t serait promu dans int et la fonction membre basic_ostream& operator<<(int n) serait appelée.

Dans le cas de std::cout << u pour un object uint8_t :

  • Le template basic_ostream& operator<<(basic_ostream&, unsigned char) serait appelé si uint8_t est une correspondance exacte pour un caractère unsigned char .
  • Sinon, comme int peut représenter toutes les valeurs de uint8_t , uint8_t serait promu dans int et la fonction membre basic_ostream& operator<<(int n) serait appelée.

Si vous voulez toujours imprimer un caractère, l'option la plus sûre et la plus claire est la suivante:

 std::cout << static_cast(i); 

Et si vous voulez toujours imprimer un numéro:

 std::cout << static_cast(i); 

int8_t exactement 8 bits de large (s’il existe).

Les seuls types entiers prédéfinis pouvant être de 8 bits sont char , unsigned char et signed char . Les short et unsigned short doivent avoir au moins 16 bits.

Donc, int8_t doit être un typedef pour le caractère signed char ou le caractère simple (ce dernier si le caractère est signé).

Si vous souhaitez imprimer une valeur int8_t sous la forme d’un entier plutôt que d’un caractère, vous pouvez le convertir explicitement en int .

En principe, un compilateur C ++ pourrait définir un type entier étendu de 8 bits (appelé peut-être quelque chose comme __int8 ), et en faire un typedef pour int8_t . La seule raison pour laquelle je peux penser à le faire serait d’éviter de faire de int8_t un type de caractère. Je ne connais aucun compilateur C ++ qui l’a fait.

Les int8_t et entier étendu ont été introduits dans C99. Pour C, il n’y a pas de raison particulière de définir un type entier étendu de 8 bits lorsque les types de caractères sont disponibles.

MISE À JOUR :

Je ne suis pas entièrement à l’aise avec cette conclusion. int8_t et uint8_t ont été introduits dans C99. En C, peu importe qu’il s’agisse ou non de types de caractères; il n’y a pas d’opérations pour lesquelles la distinction fait une réelle différence. (Même putc() , la routine de sortie de caractère de niveau le plus bas dans la norme C, prend le caractère à imprimer comme argument int ). int8_t et uint8_t , s’ils sont définis, seront presque certainement définis comme des types de caractères – mais les types de caractères ne sont que de petits types entiers.

C ++ fournit des versions surchargées spécifiques d’ operator<< pour char , char signed char et caractère unsigned char , de sorte que std::cout << 'A' et std::cout << 65 produisent des résultats très différents. Plus tard, C ++ a adopté int8_t et uint8_t , mais de telle manière que, comme dans C, ils sont presque certainement des types de caractères. Pour la plupart des opérations, ce n'est pas plus important que dans C, mais pour std::cout << ... cela fait une différence, car ceci:

 uint8_t x = 65; std::cout << x; 

imprimera probablement la lettre A plutôt que le nombre 65 .

Si vous voulez un comportement cohérent, ajoutez une dissortingbution:

 uint8_t x = 65; std::cout << int(x); // or static_cast(x) if you prefer 

Je pense que la racine du problème est qu'il manque quelque chose dans le langage: des types entiers très étroits qui ne sont pas des types de caractères.

Quant à l' intention , je pouvais spéculer que les membres du comité n'avaient pas réfléchi à la question ou décidé que cela ne valait pas la peine d'être abordé. On pourrait argumenter (et je le ferais) que les avantages d'append les types [u]int*_t à la norme l'emportent sur les inconvénients de leur comportement plutôt étrange avec std::cout << ...

Le brouillon de travail que j’ai, N3376, spécifie dans [cstdint.syn] § 18.4.1 que les types int sont typiquement des typedefs.

 namespace std { typedef signed integer type int8_t; // optional typedef signed integer type int16_t; // optional typedef signed integer type int32_t; // optional typedef signed integer type int64_t; // optional typedef signed integer type int_fast8_t; typedef signed integer type int_fast16_t; typedef signed integer type int_fast32_t; typedef signed integer type int_fast64_t; typedef signed integer type int_least8_t; typedef signed integer type int_least16_t; typedef signed integer type int_least32_t; typedef signed integer type int_least64_t; typedef signed integer type intmax_t; typedef signed integer type intptr_t; // optional typedef unsigned integer type uint8_t; // optional typedef unsigned integer type uint16_t; // optional typedef unsigned integer type uint32_t; // optional typedef unsigned integer type uint64_t; // optional typedef unsigned integer type uint_fast8_t; typedef unsigned integer type uint_fast16_t; typedef unsigned integer type uint_fast32_t; typedef unsigned integer type uint_fast64_t; typedef unsigned integer type uint_least8_t; typedef unsigned integer type uint_least16_t; typedef unsigned integer type uint_least32_t; typedef unsigned integer type uint_least64_t; typedef unsigned integer type uintmax_t; typedef unsigned integer type uintptr_t; // optional } // namespace std 

Étant donné que la seule exigence est qu’il doit être de 8 bits, typedef à un caractère est acceptable.

Je répondrai à vos questions dans l’ordre inverse.

La norme spécifie-t-elle si ce type peut ou sera un type de caractère?

Réponse courte : int8_t est signed char dans les plates-formes les plus populaires (GCC / Intel / Clang sous Linux et Visual Studio sous Windows), mais peut-être autre chose.

La longue réponse suit.

La section 18.4.1 de la norme C ++ 11 fournit le synopsis de qui comprend les éléments suivants:

typedef signé type entier int8_t; //optional int8_t; //optional

Plus loin dans le même paragraphe, le paragraphe 2 dit:

L’en-tête [ ] définit toutes les fonctions, tous les types et toutes les macros de la même manière que 7.18 dans le standard C.

où C standard signifie C99 selon 1.1 / 2:

C ++ est un langage de programmation général basé sur le langage de programmation C décrit dans ISO / IEC 9899: 1999 Langages de programmation – C (ci-après dénommé le standard C ).

Par conséquent, la définition de int8_t se trouve dans la section 7.18 du standard C99. Plus précisément, la section 7.18.1.1 de la C99 dit

Le nom typedef intN_t désigne un type entier signé avec une largeur N , aucun bit de remplissage et une représentation de complément à deux. Ainsi, int8_t désigne un type entier signé avec une largeur de exactement 8 bits .

En outre, l’article 6.2.5 / 4 de la C99 dit

Il existe cinq types d’entiers signés standard , désignés sous la forme d’un caractère signé , d’un court int , d’un int , d’un long int et d’un long long int . (Ceux-ci et d’autres types peuvent être désignés de plusieurs manières supplémentaires, comme décrit au 6.7.2.) Il peut également y avoir des types d’entiers signés étendus définis par l’implémentation . Les types d’entier signé standard et étendu sont collectivement appelés types d’entiers signés .

Enfin, la section 5.2.4.2.1 de C99 impose des tailles minimales pour les types entiers signés standard. En excluant les caractères signed char , tous les autres ont une longueur d’au moins 16 bits.

Par conséquent, int8_t est un caractère signed char ou un type entier signé étendu (non standard) de 8 bits.

Glibc (la bibliothèque GNU C) et la bibliothèque Visual Studio C définissent int8_t comme un caractère signed char . Intel et Clang, au moins sous Linux, utilisent également libc et, par conséquent, la même chose s’applique à eux. Par conséquent, dans les plates-formes les plus populaires, int8_t est un caractère signed char .

Compte tenu de ce programme C ++ 11, devrais-je m’attendre à voir un numéro ou une lettre? Ou ne pas faire d’attentes?

Réponse courte : Dans les plates-formes les plus populaires (GCC / Intel / Clang sous Linux et Visual Studio sous Windows), vous verrez certainement la lettre «A». Dans d’autres plates-formes, vous pourriez voir 65 cependant. (Merci à DyP de me l’ avoir signalé.)

Dans la suite, toutes les références se rapportent au standard C ++ 11 (version actuelle N3485).

La section 27.4.1 fournit le synopsis de , en particulier la déclaration de cout :

 extern ostream cout; 

Maintenant, ostream est un typedef pour une spécialisation de template de basic_ostream selon la section 27.7.1:

 template  > class basic_ostream; typedef basic_ostream ostream; 

La section 27.7.3.6.4 fournit la déclaration suivante:

 template basic_ostream& operator<<(basic_ostream& out, signed char c); 

Si int8_t est signed char c’est cette surcharge qui va être appelée. La même section spécifie également que l’effet de cet appel est l’impression du caractère (pas le nombre).

Considérons maintenant le cas où int8_t est un type d’entier signé étendu. De toute évidence, la norme ne spécifie pas les surcharges d’ operator<<() pour les types non standard, mais grâce aux promotions et aux conversions, l'une des surcharges fournies peut accepter l'appel. En effet, int fait au moins 16 bits et peut représenter toutes les valeurs de int8_t . Ensuite, 4.5 / 1 int8_t que int8_t peut être promu en int . Par contre, 4.7 / 1 et 4.7 / 2 int8_t que int8_t peut être converti en signed char . Enfin, 13.3.3.1.1 indique que la promotion est favorisée par rapport à la conversion lors de la résolution de la surcharge. Par conséquent, la surcharge suivante (déclarée en 23.7.3.1)

basic_ostream & basic_ostream :: operator << (int n);

sera appelé. Cela signifie que ce code

 int8_t i = 65; std::cout << i; 

imprimera 65 .

Mettre à jour:

1 . Correction du post suivant le commentaire de DyP .

2 . Ajout des commentaires suivants sur la possibilité que int8_t soit un typedef pour char .

Comme indiqué précédemment, la norme C99 (Section 6.2.5 / 4 citée ci-dessus) définit 5 types d'entiers signés standard (les caractères ne font pas partie de ceux-ci) et permet aux implémentations d'append leurs onw, appelés types entiers signés non standard. Le standard C ++ renforce cette définition dans la section 3.9.1 / 2:

Il existe cinq types entiers signés standard: «char signé», «short int», «int», «long int» et «long long int» [...] Il peut également y avoir des types d'entiers signés étendus définis par l'implémentation. Les types d'entier signé standard et étendu sont collectivement appelés types d'entiers signés .

Plus loin, dans la même section, le paragraphe 7 dit:

Les types bool , char , char16_t , char32_t , wchar_t et les types entiers signés et non signés sont collectivement appelés types intégraux . Un synonyme de type intégral est un type entier .

Par conséquent, char est un type entier mais char n'est ni un type entier signé, ni un type entier non signé et la section 18.4.1 (cité ci-dessus) indique que int8_t , lorsqu'il est présent, est un typedef pour un type entier signé.

Ce qui peut être déroutant est que, selon l'implémentation, char peut prendre les mêmes valeurs qu'un caractère signed char . En particulier, char pourrait avoir un signe mais ce n'est toujours pas un caractère signed char . Ceci est explicitement dit dans la section 3.9.1 / 1:

[...] Les caractères simples, les caractères signed char et les caractères unsigned char sont trois types distincts . [...] Dans toute implémentation particulière, un object char simple peut prendre les mêmes valeurs qu'un caractère signed char ou un caractère unsigned char ; lequel est défini par la mise en œuvre.

Cela implique également que char n'est pas un type entier signé tel que défini par 3.9.1 / 2.

3 . J'admets que mon interprétation et, plus précisément, la phrase " char n'est ni un type entier signé, ni un type entier non signé" est un peu controversée.

Pour renforcer mon argument, j'aimerais append que Stephan T. Lavavej a dit la même chose ici et Johannes Schaub - litb a également utilisé la même phrase dans un commentaire sur ce post.

char / signed char / unsigned char sont trois types différents, et un caractère n’est pas toujours 8 bits. sur la plupart des plates-formes, ils sont tous des nombres entiers de 8 bits, mais std :: ostream ne définit que la version char de >> pour les comportements comme scanf("%c", ...) .