Aller au contenu principal

Les types et leur taille

Ce que ce chapitre apporte 5 points
  • Donner la taille de chaque type de base, et la lire avec sizeof.
  • Prévoir ce que devient un entier qui dépasse sa capacité.
  • Distinguer la division entière de la division réelle, et choisir celle qu'on veut.
  • Traiter un caractère pour ce qu'il est : un petit entier.
  • Prévoir ce que perd une valeur qui passe d'un type à un autre.

En Python, un entier n'a pas de limite : il grandit tant que la mémoire suit, et 2 ** 200 s'écrit comme n'importe quel calcul. C'est commode, et c'est une illusion entretenue à grands frais par l'interpréteur.

En C, un entier occupe quatre octets, ni plus ni moins. Ces quatre octets portent un peu plus de quatre milliards de valeurs distinctes, et quand un calcul sort de cet intervalle, le résultat ne devient pas faux avec un message : il devient faux en silence, et souvent négatif. Ce chapitre montre où sont les bords, ce qui se passe quand on les franchit, et pourquoi diviser deux entiers ne donne pas ce qu'on attend.

Ce que sizeof révèle

sizeof rend le nombre d'octets qu'occupe un type. C'est un des rares opérateurs du langage qui donne une réponse sur la machine plutôt que sur le calcul.

C
Une taille dépend de la machine, et celle-ci en est une
Les trois premières lignes donneraient le même résultat sur presque toutes les machines actuelles. La quatrième, non : l'interpréteur de ces pages adresse la mémoire sur quatre octets, alors qu'un ordinateur 64 bits en emploie huit pour chaque pointeur.
C'est précisément pour cela que sizeof existe. Écrire 4 à la place de sizeof(int) dans un programme revient à parier sur la machine qui l'exécutera, et ce pari finit par se perdre.
Vérification rapideon peut se reprendre

Que vaut sizeof(int) sur une machine ordinaire ?

Ce qui arrive au bord

Un int signé de quatre octets porte les valeurs de −231-2^{31} à 231−12^{31} - 1, soit de −2 147 483 648-2\,147\,483\,648 à 2 147 483 6472\,147\,483\,647. Ajouter un au plus grand ne donne pas le suivant.

C

Le compteur repart du plus petit, comme un compteur kilométrique qui repasse à zéro. Les bits de poids fort qui ne tiennent plus sont simplement perdus, et le bit qui porte le signe se retrouve allumé.

À manipuler
Dans le bloc ci-dessus, remplacer grand + 1 par grand * 2, puis par grand + 2, et relire chaque fois le résultat. Le débordement n'est pas un cas particulier de l'addition de un : c'est ce qui arrive à toute opération dont le résultat ne tient plus.
Changer ensuite char petit = 127 en char petit = 200 : la valeur stockée n'est déjà plus celle qui était écrite, parce qu'un char signé s'arrête à 127.
Le débordement ne se voit pas, il se déduit
Aucun message, aucune ligne fautive, aucune exception. Un total qui devait croître devient négatif, une durée en millisecondes recommence à zéro au bout de 24,8 jours, une taille de fichier se met à mentir au-delà de deux gigaoctets.
La parade ne consiste pas à surveiller le résultat après coup, puisqu'il est déjà faux. Elle consiste à choisir un type assez large dès le départ, ou à vérifier avant l'opération que le résultat tiendra.

Diviser deux entiers

En C, l'opération dépend du type des opérandes, pas du résultat attendu.

C

La dernière ligne est la faute la plus fréquente du langage. La division est calculée entre deux entiers, donc elle donne 3, et ce 3 est ensuite rangé dans un double qui affiche fidèlement 3.00. Le type de la variable qui reçoit n'a aucune influence sur le calcul qui la remplit.

La règle à garder. Si les deux opérandes sont entiers, l'opération est entière, et le résultat est tronqué vers zéro.
Pour obtenir une division réelle, il faut qu'au moins un des deux opérandes soit réel : écrire 2.0 plutôt que 2, ou multiplier d'abord par 1.0.

Un caractère est un nombre

Le type char ne contient pas de lettre : il contient un entier sur un octet, et l'affichage avec %c l'interprète comme le code d'un caractère.

C

La dernière ligne est la conversion que tout programme C finit par écrire : les codes des dix chiffres se suivent, donc retrancher le code de '0' donne la valeur du chiffre. Aucune fonction n'est nécessaire, et c'est bien une soustraction d'entiers.

Vérification rapideon peut se reprendre

Que vaut l'expression 9 / 2 en C, si 9 et 2 sont des int ?

Ce que perd une conversion

Ranger une valeur dans un type plus étroit ne provoque aucune erreur, et perd ce qui ne tient pas.

C

La première conversion tronque, elle n'arrondit pas : 19,99 devient 19, et 19,99 euros deviennent 19 euros. La seconde garde le dernier octet de 300, c'est-à-dire 44, et le nombre affiché n'a plus aucun rapport avec celui qui était écrit.

Vérification rapideon peut se reprendre

1.Que se passe-t-il quand un int dépasse sa plus grande valeur ?

2.Pourquoi double moyenne = 7 / 2; affiche-t-il 3.00 ?

Mettre en pratique

Le bord d'un type se calcule avant d'être rencontré.

Les bords, les restes et les conversions

  • 1.

    Quelle est la plus grande valeur d'un int signé de 4 octets ?

  • 2.

    Quelle est la plus grande valeur d'un char signé de 1 octet ?

  • 3.

    Combien vaut la division entière 17 / 5 en C ?

  • 4.

    Combien vaut 17 % 5 ?

  • 5.

    Le caractère 'A' vaut 65. Combien vaut le caractère 'E' ?

  • 6.

    Une valeur 300 rangée dans un char signé : quelle valeur reste-t-il ?

  • 7.

    Un compteur de millisecondes sur un int signé déborde au bout de combien de jours ?

Le dernier résultat est une date célèbre : un système qui compte ses millisecondes dans un int signé se détraque au bout de vingt-cinq jours environ, ce qui est bien assez long pour que personne ne s'en aperçoive avant la mise en service.

Vérification rapideon peut se reprendre

Que vaut l'expression '7' - '0' ?

Sur une carte Arduino

Le même programme, compilé pour une carte Arduino Uno, rend sizeof(int) égal à 2 et non à 4. Un int y plafonne donc à 32 767, et un compteur de millisecondes déclaré int déborde au bout d'une trentaine de secondes. C'est la démonstration la plus concrète de ce que ce chapitre établit : la taille d'un type n'appartient pas au langage mais à la machine, et seul sizeof la dit.

Pourquoi écrire sizeof(int) plutôt que 4 dans un programme ?

Parce que 4 est une réponse vraie sur la machine où le programme a été écrit, et une supposition partout ailleurs. Un int fait deux octets sur certains microcontrôleurs, et un pointeur en fait huit sur toute machine 64 bits.

Un programme qui réserve n * sizeof(int) octets réserve toujours ce qu'il faut. Un programme qui réserve n * 4 réserve la moitié de ce qu'il faut le jour où il est recompilé ailleurs, et il écrase la mémoire qui suit.

Comment calculer une moyenne correcte à partir d'un total et d'un nombre, tous deux entiers ?

En rendant réel au moins un des deux opérandes de la division :

double moyenne = total / (double) nombre;

ou, sans écriture de conversion, en multipliant d'abord par 1.0 :

double moyenne = 1.0 * total / nombre;

Ce qui ne marche pas, c'est de déclarer la variable de réception en double : la division a déjà eu lieu, entre entiers, et le résultat tronqué est arrivé avant que le type de la variable n'ait son mot à dire.

Un capteur rend des valeurs entre 0 et 1000, et le programme les accumule dans un int. Au bout de combien de mesures un débordement devient-il possible ?

Dans le pire des cas, chaque mesure vaut 1000. Le total dépasse 231−12^{31} - 1 quand le nombre de mesures dépasse 2 147 483 647/10002\,147\,483\,647 / 1000, soit environ 2,1 millions de mesures.

À une mesure par seconde, cela laisse vingt-quatre jours. À cent mesures par seconde, cinq heures et demie. Le calcul se fait avant d'écrire le programme, et il décide du type : un long long, ou une moyenne glissante qui ne cumule jamais.

Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?

Dans la bibliothèque d'exercices

Les bords d'un int, la division entière, le caractère qui est un nombre, et la conversion qui tronque.

Tous les exercices sur les types et leur taille