web-dev-qa-db-fra.com

Les types int8_t et uint8_t sont-ils destinés à être des types char?

Étant donné ce programme C++ 11, dois-je m'attendre à voir un chiffre ou une lettre? Ou pas faire des attentes?

#include <cstdint>
#include <iostream>

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?

57
Drew Dormann

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

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

Le § 3.9.1 [basic.fundamental] stipule:

Il existe cinq types d'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 entiers signés standard et étendus sont appelés collectivement types entiers signés .

...

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

Le § 3.9.1 stipule également:

Dans toute implémentation particulière, un objet char ordinaire peut prendre les mêmes valeurs qu'un signed char Ou un unsigned char; lequel est défini par l'implémentation.

Il est tentant de conclure que int8_t Peut être un typedef de char à condition que char les objets 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 entiers signés standard et éventuellement étendus) . Voir aussi commentaires de Stephan T. Lavavej sur std::make_unsigned Et std::make_signed.

Par conséquent, soit int8_t Est une typedef de signed char Soit un type entier signé étendu dont les objets occupent exactement 8 bits de stockage.

Cependant, pour répondre à votre question, vous ne devez 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 renvoyons 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 la mise en œuvre sélectionne parmi l'ensemble des fonctions candidates conformément au § 13.3.2 et au § 13.3.3. Nous regardons ensuite au § 13.3.3.2 [over.ics.rank] pour déterminer que:

  • Le modèle template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, signed char) serait appelé si int8_t Est une correspondance exacte pour signed char (C'est-à-dire un typedef de signed char).
  • Sinon, la int8_t Serait promue en int et la fonction membre basic_ostream<charT,traits>& operator<<(int n) serait appelée.

Dans le cas de std::cout << u Pour u un objet uint8_t:

  • Le modèle template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char) serait appelé si uint8_t Est une correspondance exacte pour unsigned char.
  • Sinon, puisque int peut représenter toutes les valeurs de uint8_t, Le uint8_t Serait promu en int et la fonction membre basic_ostream<charT,traits>& operator<<(int n) serait appelé.

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

std::cout << static_cast<signed char>(i);

Et si vous voulez toujours imprimer un numéro:

std::cout << static_cast<int>(i);
25
Daniel Trebbien

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

Les seuls types d'entiers prédéfinis pouvant être de 8 bits sont char, unsigned char Et signed char. short et unsigned short Doivent tous deux être d'au moins 16 bits.

Donc int8_t Doit être un typedef pour signed char Ou plain char (ce dernier si plain char est signé).

Si vous souhaitez imprimer une valeur int8_t Sous forme d'entier plutôt que de caractère, vous pouvez la convertir explicitement en int.

En principe, un compilateur C++ pourrait définir un type entier étendu 8 bits (peut-être appelé quelque chose comme __int8), Et créer int8_t Un typedef pour cela. La seule raison pour laquelle j'y pense serait d'éviter de faire de int8_t Un type de caractère. Je ne connais aucun compilateur C++ qui l'ait fait.

int8_t Et les types entiers étendus ont été introduits dans C99. Pour C, il n'y a pas de raison particulière de définir un type entier étendu 8 bits lorsque les types char 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 de types de caractères ou non; 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ères de niveau le plus bas en C standard, 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 de operator<< Pour char, signed char Et unsigned char, De sorte que std::cout << 'A' Et std::cout << 65 produire une sortie très différente. Plus tard, C++ a adopté int8_t Et uint8_t, Mais de telle manière que, comme en C, ce sont presque certainement des types de caractères. Pour la plupart des opérations, cela n'a pas plus d'importance qu'en 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 souhaitez un comportement cohérent, ajoutez une distribution:

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

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

Quant à l'intention , je pouvais supposer que les membres du comité n'avaient pas réfléchi à la question ou avaient décidé qu'elle ne valait pas la peine d'être abordée. On pourrait soutenir (et je dirais) que les avantages de l'ajout des types [u]int*_t À la norme l'emportent sur les inconvénients de leur comportement plutôt étrange avec std::cout << ....

23
Keith Thompson

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 Sur les plateformes les plus populaires (GCC/Intel/Clang sur Linux et Visual Studio sous Windows) mais pourrait être autre chose dans d'autres.

La longue réponse suit.

La section 18.4.1 de la norme C++ 11 fournit le synopsis de <cstdint> Qui comprend les éléments suivants

typedef type entier signéint8_t; //optional

Plus loin dans la même section, paragraphe 2, il est dit

L'en-tête [<cstdint>] Définit toutes les fonctions, types et macros de la même manière que 7.18 dans la norme C .

où C standard signifie C99 selon 1.1/2:

C++ est un langage de programmation à usage général basé sur le langage de programmation C tel que décrit dans ISO/IEC 9899: 1999 Langages de programmation - C (ci-après dénommé comme la norme C ).

Par conséquent, la définition de int8_t Se trouve à la section 7.18 de la norme C99. Plus précisément, la section 7.18.1.1 de C99 dit

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

De plus, la section 6.2.5/4 de C99 dit

Il existe cinq types d'entiers signés standard , désignés comme char signé , court int , int , long int et long long int . (Ces types et d'autres peuvent être désignés de plusieurs manières supplémentaires, comme décrit au 6.7.2.) Il peut également y avoir types d'entiers signés étendus définis par l'implémentation . Les types d'entiers signés standard et étendus sont appelés collectivement 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. À l'exception de signed char, Tous les autres font au moins 16 bits.

Par conséquent, int8_t Est soit signed char Soit un type entier signé étendu de 8 bits (non standard).

La bibliothèque glibc (la bibliothèque GNU C) et Visual Studio C définissent int8_t Comme 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 signed char.

Étant donné ce programme C++ 11, dois-je m'attendre à voir un chiffre ou une lettre? Ou pas faire des attentes?

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

Dans la suite, toutes les références sont à la norme C++ 11 (version actuelle, N3485).

La section 27.4.1 fournit le synopsis de <iostream>, En particulier, elle déclare la déclaration de cout:

extern ostream cout;

Maintenant, ostream est un typedef pour une spécialisation de modèle de basic_ostream Selon la section 27.7.1:

template <class charT, class traits = char_traits<charT> >
class basic_ostream;

typedef basic_ostream<char> ostream;

La section 27.7.3.6.4 contient la déclaration suivante:

template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& 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 imprime le caractère (pas le numéro).

Considérons maintenant le cas où int8_t Est un type entier signé étendu. Évidemment, la norme ne spécifie pas les surcharges de 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. Alors 4,5/1 donne que int8_t Peut être promu à int. D'un autre côté, 4.7/1 et 4.7/2 donne que int8_t Peut être converti en signed char. Enfin, 13.3.3.1.1 indique que la promotion est préférée à la conversion pendant la résolution de surcharge. Par conséquent, la surcharge suivante (déclarée au 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.

Mise à jour:

1 . Correction du message après le commentaire de DyP .

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

Comme dit précédemment, la norme C99 (section 6.2.5/4 citée ci-dessus) définit 5 types d'entiers signés standard (char n'en fait pas partie) et permet aux implémentations d'ajouter leur onw qui sont appelés entiers signés non standard les types. La norme C++ renforce cette définition dans la section 3.9.1/2:

Il existe cinq types d'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 entiers signés standard et étendus sont appelés collectivement types entiers signés .

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

Types bool, char, char16_t, char32_t, wchar_t, et les signés et non signés les types entiers sont appelés collectivement types entiers . Un synonyme de type intégral est 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 Section 18.4.1 (cité ci-dessus) dit que int8_t, Lorsqu'il est présent, est un typedef pour un type entier signé.

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

[...] Ordinaire char, signed char Et unsigned char Sont trois types distincts . [...] Dans toute implémentation particulière, un objet char simple peut prendre les mêmes valeurs qu'un signed char Ou un unsigned char; lequel est défini par l'implémentation.

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

3 . J'avoue 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 cas, je voudrais ajouter 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 poste.

6
Cassio Neri

Le projet de copie 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, alors typedef à un caractère est acceptable.

5
Rapptz