Différences entre les versions de « Programmation C/Types de base »

m
Formatage, ajout de code
m (→‎Entiers : singulier/pluriel)
m (Formatage, ajout de code)
== Entiers ==
Il y a cinq types de variables entières (« integer » en anglais) :
*<ttcode>char</ttcode> ;
*<ttcode>short int</ttcode>, ou plus simplement <ttcode>short</ttcode> ;
*<ttcode>int</ttcode> ;
*<ttcode>long int</ttcode>, ou <ttcode>long</ttcode> ;
*<ttcode>long long int</ttcode>, ou <ttcode>long long</ttcode> (ce type a été ajouté depuis la norme C99).
 
Comme évoqué en introduction, le type caractère <ttcode>char</ttcode> est particulier, et sera étudié en détail plus bas.
 
Les types entiers peuvent prendre les modificateurs <ttcode>signed</ttcode> et <ttcode>unsigned</ttcode> qui permettent respectivement d'obtenir un type signé ou non signé. Ces modificateurs ne changent pas la taille des types. Le langage ne définit pas exactement leur taille, mais définit un domaine de valeurs minimal pour chacun.
 
=== Représentation des entiers signés ===
! Borne supérieure (formule)
|-
| bgcolor="#F9F9F9" |<ttcode>signed char</ttcode>
| ≥ 8 bits
| style="background-color:#FFFFFF; text-align: right;" |-128
| style="background-color:#FFFFFF; text-align: right;" |2<sup>7</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>unsigned char</ttcode>
| ≥ 8 bits
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>8</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>short</ttcode>
| ≥ 16 bits
| style="background-color:#FFFFFF; text-align: right;" |{{formatnum:-32768}}
| style="background-color:#FFFFFF; text-align: right;" |2<sup>15</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned short</ttcode>
| ≥ 16 bits
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>16</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>int</ttcode>
| ≥ 16 bits
| style="background-color:#FFFFFF; text-align: right;" |{{formatnum:-32768}}
| style="background-color:#FFFFFF; text-align: right;" |2<sup>15</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned int</ttcode>
| ≥ 16 bits
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>16</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>long</ttcode>
| ≥ 32 bits
| style="background-color:#FFFFFF; text-align: right;" |{{formatnum:-2147483647}}
| style="background-color:#FFFFFF; text-align: right;" |2<sup>31</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned long</ttcode>
| ≥ 32 bits
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>32</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>long long</ttcode> ''(C99)''
| ≥ 64 bits
| style="background-color:#FFFFFF; text-align: right;" |{{formatnum:-9223372036854775807}}
| style="background-color:#FFFFFF; text-align: right;" |2<sup>63</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned&nbsp;long&nbsp;long</ttcode> ''(C99)''
| ≥ 64 bits
| style="background-color:#FFFFFF; text-align: right;" |0
|}
 
Cette table signifie qu'un programme peut utiliser sans problème une variable de type <ttcode>int</ttcode> pour stocker la valeur 2<sup>15</sup>-1, quel que soit le compilateur ou la machine sur laquelle va tourner le programme.
 
Par contre, une implémentation C <em>peut</em> fournir des domaines de valeurs plus larges que ceux indiqués au-dessus :
* Les domaines indiqués pour les nombres signés dans le tableau précédent sont ceux d'une implémentation par '''complément à 1''', ou par '''signe et valeur absolue'''. Pour '''le complément à 2''', la borne inférieure est de la forme -2<sup>N-1</sup>, ce qui autorise une valeur supplémentaire (ex : <ttcode>int</ttcode> de -2<sup>15</sup> à +2<sup>15</sup>-1, soit de -{{formatnum:32768}} à +{{formatnum:32767}}),
* un <ttcode>int</ttcode> implémenté sur 32 bits pourrait aller de -(2<sup>31</sup>-1) à 2<sup>31</sup>-1, par exemple<ref name="domaineEntier">La norme C contraint les domaines de valeurs des types signés entre -(2<sup>N-1</sup>-1) et 2<sup>N-1</sup>-1, où N est un entier quelconque, et les types non signés entre 0 et 2<sup>N</sup>-1.</ref>, et un programme tournant sur une telle implémentation peut alors utiliser ces valeurs, mais il perdrait en portabilité.
 
{| class="wikitable"
! Borne supérieure (formule)
|-
| bgcolor="#F9F9F9" |<ttcode>signed char</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |-128
| style="background-color:#FFFFFF; text-align: right;" |-(2<sup>7</sup>)
| style="background-color:#FFFFFF; text-align: right;" |2<sup>7</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>unsigned char</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>8</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>short</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |-{{formatnum:32768}}
| style="background-color:#FFFFFF; text-align: right;" |-(2<sup>15</sup>)
| style="background-color:#FFFFFF; text-align: right;" |2<sup>15</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned short</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>16</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>int</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |-{{formatnum:32768}}
| style="background-color:#FFFFFF; text-align: right;" |-(2<sup>15</sup>)
| style="background-color:#FFFFFF; text-align: right;" |2<sup>15</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned int</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>16</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>long</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |-{{formatnum:2147483648}}
| style="background-color:#FFFFFF; text-align: right;" |-(2<sup>31</sup>)
| style="background-color:#FFFFFF; text-align: right;" |2<sup>31</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned long</ttcode>
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |2<sup>32</sup>-1
|-----
| bgcolor="#F9F9F9" |<ttcode>long long</ttcode> ''(C99)''
| style="background-color:#FFFFFF; text-align: right;" |-{{formatnum:9223372036854775808}}
| style="background-color:#FFFFFF; text-align: right;" |-(2<sup>63</sup>)
| style="background-color:#FFFFFF; text-align: right;" |2<sup>63</sup>-1
|-
| bgcolor="#F9F9F9" |<ttcode>unsigned&nbsp;long&nbsp;long</ttcode> ''(C99)''
| style="background-color:#FFFFFF; text-align: right;" |0
| style="background-color:#FFFFFF; text-align: right;" |0
domaine(char) ≤ domaine(short) ≤ domaine(int) ≤ domaine(long) ≤ domaine(long long)
 
Cela signifie que toutes les valeurs possibles pour une variable du type <ttcode>char</ttcode> sont aussi utilisables pour les autres types ; mais aussi que, par exemple, une valeur valide pour le type <ttcode>int</ttcode> peut ne pas être représentable dans une variable de type <ttcode>short</ttcode>.
 
Si vous ne savez pas quel type donner à une variable de type entier, le type <ttcode>int</ttcode> est par défaut le meilleur choix (à condition que votre donnée ne dépasse pas 2<sup>15</sup>-1) : ce type est la plupart du temps représenté au niveau matériel par un « mot machine », c'est-à-dire qu'il est adapté à la taille que la machine peut traiter directement (il fait usuellement 32 bits sur un PC 32 bits, par exemple). Cela permet un traitement plus rapide par le matériel. De plus, beaucoup de bibliothèques (que ce soit celle fournie par le langage C ou d'autres) utilisent ce type pour passer des entiers, ce qui fait que l'utilisation de ces bibliothèques sera plus aisée.
 
Par ailleurs, un utilisateur peut connaître les domaines de valeurs exacts de sa machine en utilisant l'en-tête <code>&lt;limits.h&gt;</code>.
L'incertitude sur l'intervalle de valeur de chaque type en fonction de la machine peut s'avérer extrêmement gênante, pour ne pas dire rédhibitoire. En effet, certains programmes peuvent nécessiter un type de données de taille fixe et cependant être destinés à être portables. Pour ces programmes, les types entiers du C ne sont pas suffisants. ''Beaucoup'' d'extensions ont été rajoutées pour définir explicitement des types entiers à intervalle fixe (8, 16, 32 bits...) à partir des types de base, avec une nomenclature loin d'être homogène d'un compilateur à l'autre (ce qui, loin de résoudre le problème, ne faisait que le déplacer).
 
La norme ISO C99 décide une bonne fois pour toute de définir, dans l'en-tête [[w:en:stdint.h|&lt;stdint.h&gt;]], plusieurs nouveaux types où <ttcode>''N''</ttcode> représente un nombre entier définissant la taille requise en bit :
 
* des types implémentés optionnellement sur certaines architectures (à éviter ?) ;
** entiers signés ou non et de longueur ''N'' exacte : <ttcode>uint''N''_t</ttcode> et <ttcode>int''N''_t</ttcode> ;
** entiers pouvant contenir un pointeur : <ttcode>intptr_t</ttcode> et <ttcode>uintptr_t</ttcode> ;
* des types requis par toutes les architectures respectant la norme C99 ;
** entiers devant être plus grand que ''N'' bits au moins : <ttcode>int_least''N''_t</ttcode> et <ttcode>uint_least''N''_t</ttcode> ;
** entiers rapides à calculer et plus grand que ''N'' bits au moins : <ttcode>int_fast''N''_t</ttcode> et <ttcode>uint_fast''N''_t</ttcode> ;
** plus grand entier : <ttcode>intmax_t</ttcode> et <ttcode>uintmax_t</ttcode>.
 
Cet en-tête définit aussi des constantes pour les valeurs minimales et maximales de chaque type.
 
Par défaut, une constante numérique entière est de type <code>int</code> et, si sa valeur est trop grande pour le type <code>int</code>, elle prend celle du type « plus grand » suffisant. Comme les domaines de valeurs des types peuvent varier suivant la machine, le type effectif d'une constante peut lui aussi varier. Cela peut s'avérer problématique lors de passage de paramètres à des fonctions à nombre variable d'arguments, par exemple. À cause de cela, il est recommandé de forcer le type de la constante en le '''postfixant''' des attributs suivants :
* <ttcode>U</ttcode> : la constante est non-signée (voir la section [[Programmation C/Opérateurs|promotion]] pour comprendre les implications) ;
* <ttcode>L</ttcode> : la constante est de type <ttcode>long</ttcode> au lieu de <ttcode>int</ttcode> ;
* <ttcode>LL</ttcode> est une nouveauté C99 pour les constantes de type <ttcode>long long</ttcode>.
<ttcode>U</ttcode> peut être combiné à <ttcode>L</ttcode> et <ttcode>LL</ttcode> pour obtenir les types <ttcode>unsigned long</ttcode> et <ttcode>unsigned long long</ttcode>, respectivement. Lorsqu'une constante est suffixée, mais que sa valeur est trop grande pour le type demandé, le même processus de recherche de type « assez grand » est utilisé<ref name="typeConstateEntiere">La liste des types successifs utilisés pour déterminer le type d'une constante entière a changé entre C90 et C99. Cela n'a, quasiment tout le temps, aucune incidence, sauf pour le cas d'une constante de valeur trop grande pour le type <ttcode>long</ttcode> : en C90, le type <ttcode>unsigned long</ttcode> est utilisé, alors qu'en C99 le type <ttcode>long long</ttcode> sera utilisé. Une telle valeur sera alors signée dans un cas, et non signée dans l'autre. Dans ce cas, utiliser un suffixe adapté (<ttcode>UL</ttcode>, <ttcode>LL</ttcode> ou <ttcode>ULL</ttcode>) permet de s'assurer du type de la constante.</ref>.
 
=== Débordement ===
Sur une machine donnée, un type entier a un domaine de valeurs fixe. Considérons qu'on travaille sur un PC en 32 bits, en complément à deux : sur un tel ordinateur, le type <ttcode>int</ttcode> varie souvent de -2<sup>31</sup> à 2<sup>31</sup>-1. Cela permet de manipuler sans problème des valeurs dans ce domaine. Par contre, si on utilise des valeurs hors du domaine, par exemple 2<sup>32</sup>, et qu'on essaye de la stocker dans une variable de type <ttcode>int</ttcode> sur une telle machine, que se passe-t-il ?
 
La réponse dépend du type:
* Si on essaye d'enregistrer une valeur hors domaine dans une variable de type ''signé'' (comme dans l'exemple), la conversion n'est pas définie par le langage. Cela signifie que tout peut arriver.
* Si on essaye d'enregistrer une valeur hors domaine dans une variable de type ''non signé'' (<ttcode>unsigned long</ttcode>, par exemple), la conversion se fait modulo la valeur maximale représentable par ce type + 1.
 
Exemples : On suppose que le type <ttcode>unsigned char</ttcode> est codé sur 8 bits. Alors une valeur de ce type peut aller de 0 à 2<sup>8</sup>-1, soit 255. Par la règle précédente, les conversions se feront donc modulo 255 + 1, soit 256. On considère le programme suivant :
 
<source lang="c" line="1">
=== Constantes réelles ===
Une suite de caractères représente une constante à virgule flottante si :
*c'est une suite de chiffres séparée par un caractère « point », cette séparation pouvant s'effectuer à n'importe quel endroit de la suite (<code>0.0</code>, <code>.0</code> et <code>0.</code> représentent tous les trois la valeur 0 de type <ttcode>double</ttcode>) ;
*un ''nombre'' suivi d'un caractère « e » suivi d'un entier.
Dans le deuxième cas, le ''nombre'' peut être soit un entier, soit un réel du premier cas.
| style="background:#ECE5CA; text-align:center; font-weight:bold;" |Caractère
|-
| bgcolor="#EAF5FB" |<ttcode><nowiki>'\''</nowiki></ttcode>
| bgcolor="#FAF9EC" |une apostrophe
|-
| bgcolor="#EAF5FB" |<ttcode>'\"'</ttcode>
| bgcolor="#FAF9EC" |un guillemet
|-
| bgcolor="#EAF5FB" |<ttcode>'\?'</ttcode>
| bgcolor="#FAF9EC" |un point d'interrogation
|-
| bgcolor="#EAF5FB" |<ttcode>'\\'</ttcode>
| bgcolor="#FAF9EC" |un backslash
|-
| bgcolor="#EAF5FB" |<ttcode>'\a'</ttcode>
| bgcolor="#FAF9EC" |un signal sonore (ou visuel)
|-
| bgcolor="#EAF5FB" |<ttcode>'\b'</ttcode>
| bgcolor="#FAF9EC" |un espace arrière
|-
| bgcolor="#EAF5FB" |<ttcode>'\f'</ttcode>
| bgcolor="#FAF9EC" |saut au début de la page suivante
|-
| bgcolor="#EAF5FB" |<ttcode>'\n'</ttcode>
| bgcolor="#FAF9EC" |saut de ligne
|-
| bgcolor="#EAF5FB" |<ttcode>'\r'</ttcode>
| bgcolor="#FAF9EC" |un retour chariot
|-
| bgcolor="#EAF5FB" |<ttcode>'\t'</ttcode>
| bgcolor="#FAF9EC" |une tabulation (pose problème avec gcc-4.8)
|-
| bgcolor="#EAF5FB" |<ttcode>'\v'</ttcode>
| bgcolor="#FAF9EC" |une tabulation verticale (pose problème avec gcc-4.8)
|}
De plus, on peut écrire n'importe quelle valeur de caractère avec les expressions suivantes :
*<ttcode>'\x''HH'''</ttcode>, où chaque <ttcode>''H''</ttcode> représente un chiffre hexadécimal correspondant au code du caractère. Par exemple, <ttcode>'\x61'</ttcode> représente le caractère <ttcode>'a'</ttcode> (minuscule) en ASCII (car 97 = 6 * 16 + 1) ;
*<ttcode>"\xc5\x93"</ttcode> représente le caractère œ en UTF-8.
*<ttcode>'\''OOO'''</ttcode>, où chaque <ttcode>''O''</ttcode> représente un chiffre octal correspondant au code du caractère.
 
=== Trigraphe ===
! Caractère
|-
|<ttcode>??=</ttcode>
|<ttcode>#</ttcode>
|-
|<ttcode>??(</ttcode>
|<ttcode>[</ttcode>
|-
|<ttcode>??)</ttcode>
|<ttcode>]</ttcode>
|-
|<ttcode>??&lt;</ttcode>
|<ttcode>{</ttcode>
|-
|<ttcode>??&gt;</ttcode>
|<ttcode>}</ttcode>
|-
|<ttcode>??/</ttcode>
|<ttcode>\</ttcode>
|-
|<ttcode>??'</ttcode>
|<ttcode>^</ttcode>
|-
|<ttcode>??!</ttcode>
|<ttcode>&#124;</ttcode>
|-
|<ttcode>??-</ttcode>
|<ttcode>~</ttcode>
|}
Voici une manière de rendre illisible un programme utilisant les trigraphes :