Type
(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)
|
(depuis C23) |
|
(depuis C99) |
-
- types entiers non signés
-
- standard : _Bool , (since C99) unsigned char , unsigned short , unsigned int , unsigned long , unsigned long long (since C99)
|
(depuis C23) |
|
(depuis C99) |
-
- types à virgule flottante
-
- types à virgule flottante réels : float , double , long double
|
(depuis C23) |
|
(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
|
(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.
|
(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.
|
(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
|
Documentation C++
pour
Type
|