Namespaces
Variants

Type

From cppreference.net

(Voir aussi les types arithmétiques pour les détails sur la plupart des types intégrés et la liste des utilitaires liés aux types fournis par la bibliothèque C.)

Objets , fonctions , et expressions ont une propriété appelée type , qui détermine l'interprétation de la valeur binaire stockée dans un objet ou évaluée par l'expression.

Table des matières

Classification des types

Le système de types C comprend les types suivants :

  • le type void
  • types de base
  • le type char
  • types entiers signés
  • standard : signed char , short , int , long , long long (depuis C99)
  • bit-précis : _BitInt ( N ) où N est une expression constante entière qui spécifie le nombre de bits utilisés pour représenter le type, y compris le bit de signe. Chaque valeur de N désigne un type distinct.
(depuis C23)
  • étendu : défini par l'implémentation, par ex. __int128
(depuis C99)
  • types entiers non signés
  • standard : _Bool , (since C99) unsigned char , unsigned short , unsigned int , unsigned long , unsigned long long (since C99)
  • bit-précis : unsigned _BitInt ( N ) N est une expression constante entière qui spécifie le nombre de bits utilisés pour représenter le type. Chaque valeur de N désigne un type distinct. Cette catégorie inclut le type unsigned _BitInt ( 1 ) qui n'a pas de type entier signé bit-précis correspondant.
(depuis C23)
  • étendu : défini par l'implémentation, par ex. __uint128
(depuis C99)
  • types à virgule flottante
  • types à virgule flottante réels : float , double , long double
  • types de nombres flottants réels décimaux : _Decimal32 , _Decimal64 , _Decimal128
(depuis C23)
  • types complexes : float _Complex , double _Complex , long double _Complex
  • types imaginaires : float _Imaginary , double _Imaginary , long double _Imaginary
(depuis C99)
  • types dérivés
(depuis C11)

Pour chaque type listé ci-dessus, plusieurs versions qualifiées de son type peuvent exister, correspondant aux combinaisons d'un, deux ou des trois const , volatile , et restrict qualificateurs (lorsque permis par la sémantique du qualificateur).

Groupes de types

  • types d'objets : tous les types qui ne sont pas des types de fonction
  • types de caractères : char , signed char , unsigned char
  • types entiers : char , types entiers signés, types entiers non signés, types énumérés
  • types réels : types entiers et types flottants réels
  • types arithmétiques : types entiers et types flottants
  • types scalaires : types arithmétiques, types pointeurs , et nullptr_t (depuis C23)
  • types agrégés : types tableau et types structure
  • types dérivés de déclarateur : types tableau, types fonction et types pointeurs

Construire un type d'objet complet tel que le nombre d'octets dans sa représentation d'objet n'est pas représentable dans le type size_t (c'est-à-dire le type résultant de l'opérateur sizeof ) , y compris la formation d'un tel type VLA à l'exécution, (depuis C99) est un comportement indéfini.

Types compatibles

Dans un programme C, les déclarations faisant référence au même objet ou fonction dans différentes unités de traduction n'ont pas besoin d'utiliser le même type. Elles doivent seulement utiliser des types suffisamment similaires, formellement appelés types compatibles . La même règle s'applique aux appels de fonction et aux accès lvalue ; les types d'arguments doivent être compatibles avec les types de paramètres et le type d'expression lvalue doit être compatible avec le type d'objet auquel on accède.

Les types T et U sont compatibles, si

  • ils sont du même type (même nom ou alias introduit par un typedef )
  • ils sont des versions identiquement qualifiées cvr de types non qualifiés compatibles
  • ils sont des types pointeur et pointent vers des types compatibles
  • ils sont des types tableau, et
  • leurs types d'éléments sont compatibles, et
  • si les deux ont une taille constante, cette taille est identique. Note : les tableaux de taille inconnue sont compatibles avec tout tableau de type d'élément compatible. VLA est compatible avec tout tableau de type d'élément compatible. (since C99)
  • ils sont tous deux des types structure/union/énumération, et
  • (C99) si l'un est déclaré avec un tag, l'autre doit également être déclaré avec le même tag.
  • si les deux sont des types complets, leurs membres doivent correspondre exactement en nombre, être déclarés avec des types compatibles, et avoir des noms correspondants.
  • de plus, s'il s'agit d'énumérations, les membres correspondants doivent également avoir les mêmes valeurs.
  • de plus, s'il s'agit de structures ou d'unions,
  • Les membres correspondants doivent être déclarés dans le même ordre (structures uniquement)
  • Les champs de bits correspondants doivent avoir les mêmes largeurs.
  • l'un est un type énuméré et l'autre est le type sous-jacent de cette énumération
  • ce sont des types de fonctions, et
  • leurs types de retour sont compatibles
  • ils utilisent tous deux des listes de paramètres, le nombre de paramètres (y compris l'utilisation de l'ellipse) est le même, et les paramètres correspondants, après application des ajustements de type tableau-vers-pointeur et fonction-vers-pointeur et après suppression des qualificateurs de plus haut niveau, ont des types compatibles
  • l'une est une définition de style ancien (sans paramètres), l'autre a une liste de paramètres, la liste de paramètres n'utilise pas d'ellipse et chaque paramètre est compatible (après ajustement du type de paramètre de fonction) avec le paramètre de style ancien correspondant après promotions d'arguments par défaut
  • l'une est une déclaration de style ancien (sans paramètres), l'autre a une liste de paramètres, la liste de paramètres n'utilise pas d'ellipse, et tous les paramètres (après ajustement du type de paramètre de fonction) ne sont pas affectés par les promotions d'arguments par défaut
(jusqu'en C23)

Le type char n'est pas compatible avec signed char et n'est pas compatible avec unsigned char .

Si deux déclarations font référence au même objet ou fonction et n'utilisent pas des types compatibles, le comportement du programme est indéfini.

// Unité de traduction 1
struct S { int a; };
extern struct S *x; // compatible avec le x de l'UT2, mais pas avec celui de l'UT3
// Unité de traduction 2
struct S;
extern struct S *x; // compatible avec les deux x
// Unité de traduction 3
struct S { float a; };
extern struct S *x; // compatible avec le x de l'UT2, mais pas avec celui de l'UT1
// le comportement est indéfini
// Unité de Traduction 1
#include <stdio.h>
struct s { int i; }; // compatible avec le s de UT3, mais pas avec celui de UT2
extern struct s x = {0}; // compatible avec le x de UT3
extern void f(void); // compatible avec le f de UT2
int main()
{
    f();
    return x.i;
}
// Unité de Traduction 2
struct s { float f; }; // compatible avec le s de UT4, mais pas avec celui de UT1
extern struct s y = {3.14}; // compatible avec le y de UT4
void f() // compatible avec le f de UT1
{
    return;
}
// Unité de Traduction 3
struct s { int i; }; // compatible avec le s de UT1, mais pas avec celui de UT2
extern struct s x; // compatible avec le x de UT1
// Unité de Traduction 4
struct s { float f; }; // compatible avec le s de UT2, mais pas avec celui de UT1
extern struct s y; // compatible avec le y de UT2
// le comportement est bien défini : seules les déclarations multiples
// d'objets et de fonctions doivent avoir des types compatibles, pas les types eux-mêmes

Note : Le C++ n'a pas de notion de types compatibles. Un programme C qui déclare deux types compatibles mais non identiques dans différentes unités de traduction n'est pas un programme C++ valide.

Types composites

Un type composite peut être construit à partir de deux types compatibles ; c'est un type qui est compatible avec les deux types et qui satisfait aux conditions suivantes :

  • Si les deux types sont des types tableau, les règles suivantes sont appliquées :
  • Si un type est un tableau de taille constante connue, le type composite est un tableau de cette taille.
  • Sinon, si un type est un VLA dont la taille est spécifiée par une expression non évaluée, un programme nécessitant le type composite des deux types a un comportement indéfini.
  • Sinon, si un type est un VLA dont la taille est spécifiée, le type composite est un VLA de cette taille.
  • Sinon, si un type est un VLA de taille non spécifiée, le type composite est un VLA de taille non spécifiée.
(depuis C99)
  • Sinon, les deux types sont des tableaux de taille inconnue et le type composite est un tableau de taille inconnue.
Le type d'élément du type composite est le type composite des deux types d'éléments.
  • Si un seul type est un type de fonction avec une liste de types de paramètres (un prototype de fonction), le type composite est un prototype de fonction avec la liste de types de paramètres.
(jusqu'à C23)
  • Si les deux types sont des types de fonction avec des listes de types de paramètres, le type de chaque paramètre dans la liste de types de paramètres composite est le type composite des paramètres correspondants.

Ces règles s'appliquent récursivement aux types dont dérivent les deux types.

// Étant donné les deux déclarations de portée de fichier suivantes :
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]); // C23: Erreur : types conflictuels pour 'f'
// Le type composite résultant pour la fonction est :
int f(int (*)(char *), double (*)[3]);

Pour un identifiant ayant une liaison interne ou externe déclaré dans une portée où une déclaration antérieure de cet identifiant est visible, si la déclaration antérieure spécifie une liaison interne ou externe, le type de l'identifiant lors de la déclaration ultérieure devient le type composite.

Types incomplets

Un type incomplet est un type d'objet qui manque d'informations suffisantes pour déterminer la taille des objets de ce type. Un type incomplet peut être complété à un certain moment dans l'unité de traduction.

Les types suivants sont incomplets :

  • le type void . Ce type ne peut pas être complété.
  • type tableau de taille inconnue. Il peut être complété par une déclaration ultérieure qui spécifie la taille.
extern char a[]; // le type de a est incomplet (ceci apparaît généralement dans un en-tête)
char a[10];      // le type de a est maintenant complet (ceci apparaît généralement dans un fichier source)
  • structure ou union de contenu inconnu. Elle peut être complétée par une déclaration de la même structure ou union qui définit son contenu ultérieurement dans la même portée.
struct node
{
    struct node* next; // la structure node est incomplète à ce stade
}; // la structure node est complète à ce stade

Noms de types

Un type peut devoir être nommé dans un contexte autre qu'une déclaration . Dans ces situations, un nom de type est utilisé, qui est grammaticalement exactement identique à une liste de spécificateurs de type et qualificateurs de type , suivie du déclarateur (voir déclarations ) tel qu'il serait utilisé pour déclarer un objet unique ou une fonction de ce type, sauf que l'identifiant est omis :

int n; // déclaration d'un int
sizeof(int); // utilisation d'un nom de type
int *a[3]; // déclaration d'un tableau de 3 pointeurs vers int
sizeof(int *[3]); // utilisation d'un nom de type
int (*p)[3]; // déclaration d'un pointeur vers un tableau de 3 int
sizeof(int (*)[3]); // utilisation d'un nom de type
int (*a)[*] // déclaration d'un pointeur vers VLA (dans un paramètre de fonction)
sizeof(int (*)[*]) // utilisation d'un nom de type (dans un paramètre de fonction)
int *f(void); // déclaration de fonction
sizeof(int *(void)); // utilisation d'un nom de type
int (*p)(void); // déclaration d'un pointeur vers fonction
sizeof(int (*)(void)); // utilisation d'un nom de type
int (*const a[])(unsigned int, ...) = {0}; // tableau de pointeurs vers fonctions
sizeof(int (*const [])(unsigned int, ...)); // utilisation d'un nom de type

Sauf que les parenthèses redondantes autour de l'identifiant sont significatives dans un nom de type et représentent "fonction sans spécification de paramètre" :

int (n); // déclare n de type int
sizeof(int ()); // utilise le type "fonction retournant int"

Les noms de type sont utilisés dans les situations suivantes :

(depuis C99)
(depuis C11)


Un nom de type peut introduire un nouveau type :

void* p = (void*)(struct X { int i; } *)0;
// le nom de type "struct X {int i;}*" utilisé dans l'expression de cast
// introduit le nouveau type "struct X"
struct X x = {1}; // struct X est maintenant dans la portée

Références

  • Norme C23 (ISO/CEI 9899:2024) :
  • 6.2.5 Types (p: TBD)
  • 6.2.6 Représentations des types (p: TBD)
  • 6.2.7 Type compatible et type composite (p: TBD)
  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.2.5 Types (p : 31-33)
  • 6.2.6 Représentations des types (p : 31-35)
  • 6.2.7 Type compatible et type composite (p : 35-36)
  • Norme C11 (ISO/IEC 9899:2011) :
  • 6.2.5 Types (p: 39-43)
  • 6.2.6 Représentations des types (p: 44-46)
  • 6.2.7 Type compatible et type composite (p: 47-48)
  • Norme C99 (ISO/CEI 9899:1999) :
  • 6.2.5 Types (p: 33-37)
  • 6.2.6 Représentations des types (p: 37-40)
  • 6.2.7 Type compatible et type composite (p: 40-41)
  • Norme C89/C90 (ISO/CEI 9899:1990) :
  • 3.1.2.5 Types
  • 3.1.2.6 Type compatible et type composite

Voir aussi