Bases du langage C
Introduction
Ce chapitre a pour objet de présenter quelques éléments du langage C. Si vous n’avez pas ou peu de notions de C, nous vous conseillons de le lire de façon linéaire, comme si chaque recette était une leçon. Nous avons gardé la structure du livre basée sur des recettes car outre la cohérence avec le reste de l’ouvrage, cette présentation permet de poser le problème, de donner un résumé de la leçon dans la solution, et de détailler cette solution dans la discussion.
Ce chapitre a également pour vocation de rappeler certains points de base à ceux qui veulent se rafraîchir la mémoire, et même de servir de référence en particulier avec les tableaux récapitulatifs de certaines recettes.
Écrire son premier programme
Problème
Vous souhaitez écrire votre premier programme ou écrire le squelette de votre prochain programme.
Solution
Recopiez le programme suivant dans votre éditeur de texte et enregistrez-le dans un fichier d’extension .c, par exemple bonjour.c.
#include <stdio.h>
#include <stdlib.h>
int
main ()
{
printf ("Bonjour\n");
exit (EXIT_SUCCESS);
}
Discussion
Votre premier programme affiche Bonjour. Il est composé des éléments suivants :
-
le programme principal (printf() et exit()) contenu dans une fonction qui porte toujours le nom main ;
-
un appel à printf() pour afficher le message Bonjour ;
-
un appel à exit() pour mettre fin à notre programme ;
-
l’inclusion de deux fichiers dit d’en-têtes stdio.h et stdlib.h. Ils contiennent entre autres la déclaration des deux fonctions printf() et exit().
Ce programme est le premier présenté aux débutants. C’est également la base de tout nouveau programme, que vous soyez débutant ou expert. Lorsque vous démarrerez un nouveau projet, nous vous conseillons d’écrire ce programme et de le compiler pour vous assurer dès le début que vos outils de développement et de compilation sont opérationnels. Pour compiler ce programme, référez-vous...
Commenter du code
Problème
Vous voulez mettre des commentaires dans votre code source ou vous voulez désactiver la compilation de certaines lignes de code.
Solution
Encadrez vos commentaires ainsi : /* vos commentaires ici */. Dans le cas où vous voudriez commenter une section de code, faites précéder des lignes de code à désactiver par #ifdef NEPASCOMPILER et terminez cette section de code avec #endif.
Discussion
Commenter du code peut généralement signifier deux choses très distinctes :
-
ajouter des commentaires afin d’expliciter le code ;
-
faire ignorer une section de code au compilateur afin de la désactiver.
Le langage C permet d’ajouter des commentaires avec /* et */. Tout ce qui se trouve entre ces deux séquences sera ignoré par le compilateur. Il est également possible de faire précéder des commentaires par // et tout ce qui suit sur la ligne sera également ignoré. Les // sont issus des compilateurs C++ mais la plupart des compilateurs C les reconnaissent également. Voici quelques exemples :
/* un commentaire sur une ligne */
a = 5; /* un commentaire après du code */
// un commentaire sur une ligne
a = 5; // tout ce qui suit est ignoré
/* un commentaire
sur
plusieurs lignes
avec une indentation
décorative. */ ...
Utiliser des variables et des chaînes de caractères
Problème
Vous voulez placer certaines valeurs dans des variables et utiliser celles-ci dans votre programme.
Solution
Définissez des variables avec un type, un nom et une valeur initiale éventuelle.
Discussion
Une variable se définit d’une des deux façons suivantes en C :
type nom;
type nom = valeur_initiale;
Les types de base sont les suivants :
Type |
Taille en octets |
Signification |
char (unsigned char) |
1 |
caractère |
short (unsigned short) |
2 |
entier court |
int (unsigned int) |
2, 4 ou 8 |
entier |
long (unsigned long) |
4 ou 8 |
entier long |
long long (unsigned long long) |
8 |
entier long sur 64 bits (à partir de la norme ISO C99) |
float |
4 |
flottant |
double |
8 |
flottant double précision |
long double |
16 |
flottant quadruple précision |
void |
Non applicable |
absence de type (utilisé par les fonctions qui ne renvoient pas d’argument) |
Les types int et long dépendent du modèle de représentation des données du compilateur, qui est conditionné par votre ordinateur et votre système d’exploitation. Ainsi, sur un ordinateur 32 bits, avec un système d’exploitation 32 bits, les deux types int et long sont de 4 octets. Sur un ordinateur 64 bits avec un système d’exploitation 64 bits, le modèle de représentation des données peut varier et vous pouvez avoir 4 ou 8 octets pour ces deux types. Voyez les options de votre compilateur pour savoir comment lui spécifier le modèle de représentation des données à utiliser. Sachant que l’opérateur sizeof indique la taille d’un type, la règle est la suivante :
sizeof(short) <= sizeof(int) <= sizeof(long)
Voici un tableau...
Définir et utiliser des constantes
Problème
Plutôt que de coder « en dur » une valeur qui ne change pas tout au long du programme, vous voudriez définir et utiliser une constante.
Solution
Utilisez #define CONSTANTE valeur où CONSTANTE est un nom de constante, suivi de sa valeur. Si vous comptez définir plusieurs constantes représentant l’ensemble des valeurs possibles pour une variable, préférez l’utilisation d’une énumération avec le mot-clé enum.
Discussion
L’opérateur #define sert à définir des macros qui sont remplacées par le préprocesseur dans le code source avant la phase de compilation. Vous pouvez utiliser cela pour une simple valeur (nombre, chaîne de caractères). Dans ce cas, l’usage veut que cette macro soit appelée constante. C’est un moyen élégant de définir une constante car il procure les avantages suivants :
-
Le compilateur travaille sur la valeur. La constante est donc évaluée à la compilation (car remplacée au préalable par le préprocesseur) et non à l’exécution de votre programme (contrairement à une variable).
-
Certains programmeurs alternent l’utilisation d’une constante et sa valeur. Si vous vous astreignez à utiliser uniquement une constante, vous vous garantissez...
Utiliser les opérations de base
Problème
Vous souhaitez appliquer des opérations à vos variables.
Solution
Utilisez les opérateurs du langage C :
Opérateur |
Signification |
() |
Change la priorité des opérations ou délimite les arguments à l’appel d’une fonction |
[] |
Délimite l’indice pour obtenir un élément de tableau |
. |
Indique un champ de structure ou d’union |
-> |
Indique un champ désigné par pointeur |
! |
Opérateur négation booléenne |
~ |
Opérateur complément binaire |
- |
Opérateur opposé |
++ |
Opérateur d’incrémentation |
-- |
Opérateur de décrémentation |
& |
Permet d’obtenir l’adresse d’une variable et non son contenu |
* |
Permet d’accéder aux données pointées par un pointeur |
(type) |
Transtypage (cast) |
sizeof |
Opérateur de taille : renvoie la taille de l’expression (l’unité est la taille du type char. Aussi, sizeof(char) vaut 1 par définition). |
* |
Opérateur de multiplication |
/ |
Opérateur de division |
% |
Opérateur modulo (reste de la division euclidienne) |
+ |
Opérateur d’addition |
- |
Opérateur de soustraction |
<< |
Opérateur décalage à gauche |
>> |
Opérateur décalage à droite |
< |
Opérateur de test : strictement inférieur |
<= |
Opérateur de test : inférieur ou égal |
> |
Opérateur de test : strictement supérieur |
>= |
Opérateur de test : supérieur ou égal |
== |
Opérateur de test : égal |
!= |
Opérateur de test : différent |
& |
Opérateur ET binaire |
ˆ |
Opérateur OU exclusif (XOR) binaire |
| |
Opérateur OU binaire |
&& |
Opérateur ET booléen |
|| |
Opérateur OU booléen |
? : |
Opérateur conditionnel |
= |
Affectation simple |
*= /= %= |
Affectations précédées d’une multiplication, d’une division ou du modulo |
+= -= <<= |
Affectations précédées d’une addition, d’une soustraction ou d’un décalage à gauche |
>>= &= |= ˆ= |
Affectations précédées d’un décalage à droite, d’un ET binaire, d’un OU binaire ou d’un OU eXclusif... |
Utiliser les opérateurs mathématiques
Problème
Vous voulez exécuter une opération mathématique non définie par les opérateurs de base (vus dans la recette précédente).
Solution
Utilisez les fonctions de la bibliothèque mathématique définies dans le fichier d’en-têtes math.h.
Discussion
Les fonctions mathématiques ne font pas partie du langage C. Comme elles sont nécessaires à de nombreux programmeurs, elles ont été implémentées dans la bibliothèque mathématique. Avec certains compilateurs comme gcc, celle-ci est implicitement liée. Avec d’autres compilateurs (comme Sunstudio dans la version 2008.11 d’Opensolaris), vous devrez l’indiquer explicitement à l’éditeur de liens avec l’option -lm.
Votre code devra inclure le fichier d’en-têtes math.h avec une ligne comme celle-ci :
#include <math.h>
Le programme suivant calcule et affiche le sinus de π/4 et la racine carrée de 2 divisée par 2. Ces deux valeurs sont normalement égales. C’est ce que nous constatons à l’exécution de ce programme. Vous noterez que la valeur de π est définie dans une constante M_PI dans math.h et que nous en profitons ici :
#include <stdio.h>
#include <stdlib.h>
#include <math.h> ...
Afficher une expression
Problème
Vous voulez afficher une chaîne de caractères ou un résultat à l’écran.
Solution
Utilisez la fonction printf().
Discussion
La fonction printf() prend un premier argument indiquant le format des données à afficher. Les arguments suivants sont ces données. Voici deux exemples, le premier affichant une chaîne de caractères et le second le contenu d’une variable précédé du nom de cette variable :
printf ("Bonjour tout le monde\n");
printf ("La variable nb contient %d\n", nb);
La syntaxe du premier argument est une chaîne dans laquelle le contenu des variables (les arguments suivants) à afficher est indiqué par un caractère % suivi de leur format d’affichage. Dans l’exemple précédent, la variable nb doit être affichée comme un entier décimal comme l’indique %d.
Le format d’affichage des variables s’écrit ainsi :
%[attributs][largeur minimale de champ][.précision][modificateur
de longueur][indicateur de conversion]
Après le caractère %, les champs sont optionnels à l’exception du dernier qui est obligatoire.
Les attributs
Les attributs sont les suivants :
-
# : la valeur est convertie sous une autre forme. Voyez le tableau sur les indicateurs de conversion plus loin.
-
0 : utilise le chiffre 0 à la place du caractère espace comme caractère de remplissage.
-
- : justifie l’affichage à gauche (il l’est à droite par défaut).
-
[caractère espace] : si le nombre à afficher est positif, il sera précédé d’un caractère espace.
-
+ : si le nombre à afficher est positif, il sera précédé d’un caractère +.
La largeur minimale
La largeur minimale de champ est le nombre de caractères minimal à utiliser pour afficher le résultat. Par exemple, le nombre 29 est affiché avec 2 caractères pour %d et 4 caractères (dont deux espaces) pour %4d. Par contre, le nombre 81129 nécessitera 5 caractères aussi bien avec %d que %4d. La largeur minimale ne doit en aucun cas commencer par 0. Cela n’a aucun intérêt et prêterait à...
Exécuter du code de façon conditionnelle
Problème
Vous voulez exécuter une section de code sous certaines conditions.
Solution
Utilisez une des instructions de test suivantes :
if (condition)
instruction_si_condition_remplie;
if (condition)
instruction_si_condition_remplie;
else
instruction_si_condition_non_remplie;
if (condition1)
instruction_si_condition1_remplie;
else if (condition2)
instruction_si_condition2_remplie;
else if (condition3)
instruction_si_condition3_remplie;
else
instruction_si_aucune_condition_remplie;
switch (expression)
{
case valeur1:
instruction1_si_valeur1;
instruction2_si_valeur1;
break;
case valeur2:
instruction1_si_valeur2;
instruction2_si_valeur2;
break;
default:
instruction1_si_aucune_valeur;
instruction2_si_aucune_valeur;
}
Vous pouvez également vous servir de l’opérateur conditionnel condition ? valeur1 : valeur2.
Discussion
Pour exécuter du code en fonction d’une condition, le plus simple est d’utiliser l’opérateur if (condition). Si la condition est remplie, l’instruction suivante (ou le bloc d’instructions) est exécuté, sinon, il est ignoré.
L’opérateur if (condition) accepte une deuxième partie avec le mot-clé else pour exécuter l’instruction (ou le bloc d’instructions) qui le suit si la condition n’est pas remplie. Voici un exemple :
if(age < 18)
printf("Mineur\n"); ...
Exécuter du code en boucle
Problème
Vous voulez qu’une section de code soit exécutée plusieurs fois.
Solution
Écrivez une boucle à l’aide d’une des constructions suivantes :
while (condition)
{
instructions
}
do
{
instructions
}
while (condition);
for (initialisation; condition; instruction)
{
instructions
}
Discussion
Les syntaxes while, do/while et for permettent d’exécuter du code en boucle. Le principe est de placer des instructions à exécuter tant que la condition est vérifiée. La boucle prend fin dès que la condition ne l’est plus.
while et do/while
Les boucles while et do/while fonctionnent sur le même principe, énoncé ci-dessus. Leur seule différence est que while vérifie la condition avant d’exécuter les instructions alors que do/while la vérifie après. En d’autres termes, les instructions sont exécutées au moins une fois avec do/while alors que, si la condition n’est pas remplie pour la boucle while, les instructions ne sont pas exécutées, pas même une première fois. Voici deux exemples qui illustrent cette différence :
int i = 0;
while (i > 0)
{
printf ("%d\n", i);
i--;
}
Dans cet exemple...
Définir et utiliser un tableau
Problème
Vous disposez d’une liste de valeurs d’un même type. Vous connaissez le nombre de ces variables ou au moins leur nombre maximal. Vous voulez stocker ces valeurs mais ne souhaitez pas utiliser une variable pour chacune de ces valeurs.
Solution
Utilisez un tableau ou une structure de liste chaînée.
Discussion
Un tableau est, en C, un espace mémoire dont la taille est égale à n fois la taille du type du tableau, n étant le nombre d’éléments du tableau. Chaque élément a le même type. Il est donc possible d’obtenir le n-ième élément du tableau en indiquant son nom et l’indice correspondant en le spécifiant entre crochets. Voyez ces exemples qui montrent également comment déclarer un tableau :
/* Déclaration des tableaux */
char caractere[5];
int nombre[10];
/* Initialisation des tableaux */
for (i = 0; i < 5; i++)
caractere[i] = 'a' + i;
for (i = 0; i < 10; i++)
nombre[i] = i;
/* Utilisation des tableaux */
printf ("3ème élément de caractere : %c\n", caractere[2]);
printf ("7ème élément de nombre : %d\n", nombre[6]);
En C, la numérotation des indices des éléments d’un tableau commence toujours à zéro. Le premier élément est donc l’élément numéro 0.
Lorsque vous déclarez un tableau, vous indiquez son type, son nom et son nombre d’éléments. Le nombre d’éléments peut être implicite si vous initialisez le tableau en même temps que vous le déclarez. Dans ce cas, le nombre d’éléments...
Définir et utiliser une structure
Problème
Certaines de vos données, quel que soit leur type, devraient être regroupées afin d’être traitées ensemble.
Solution
Une structure permet de définir des champs pour une seule et même variable. Créez une structure puis déclarez une variable dont le type est cette structure. Vous pourrez alors affecter vos données aux différents champs de votre variable.
Discussion
Une structure se définit avec le mot-clé struct suivi du nom de la structure. Ensuite, entre accolades, viennent les définitions des champs en respectant une syntaxe similaire à celle de la définition des variables. Voici un exemple de définition de structure :
struct point
{
double x;
double y;
unsigned short couleur;
char *nom;
};
Pour déclarer une variable avec ces champs, considérez la structure comme un nouveau type :
struct point origine;
Vous pouvez initialiser la variable dès sa déclaration :
struct point origine = { 0.0, 0.0, 0xFFFF, "origine" };
Pour accéder à un champ de la structure, indiquez les noms de la variable et du champ en les séparant par un point. Nous affichons ci-dessous le contenu de notre variable origine :
printf ("'%s' = (%f , %f) / %d\n", origine.nom, origine.x...
Définir et utiliser un pointeur
Problème
Au lieu de manipuler certaines données parfois complexes, vous voulez disposer d’une référence à celles-ci.
Solution
Un pointeur est une référence à une variable. En utilisant un pointeur, vous pouvez décider ce sur quoi il pointe ainsi que lire et modifier les données pointées.
Discussion
Avant de discuter l’utilisation des pointeurs, rappelons une règle fondamentale relative à l’utilisation de ceux-ci : un pointeur doit toujours être initialisé. Cela est important car de nombreux bogues et arrêts intempestifs de programmes proviennent d’un oubli d’initialisation d’un pointeur. Vous pouvez initialiser un pointeur à NULL en attendant de lui affecter une adresse plus significative.
Un pointeur se déclare de la même façon qu’une variable sur laquelle il peut pointer. La seule différence est que dans la déclaration, il faut précéder le nom du pointeur par une étoile. Voici un exemple de pointeur susceptible de pointer sur une variable de type double :
double *valeur;
L’affectation d’une adresse à un pointeur s’effectue comme celle d’une valeur à une variable :
int *a;
int *b;
b = a;
Dans cet exemple, a et b sont deux pointeurs. La conséquence de l’affectation est que ces deux pointeurs vont pointer sur le même contenu.
Il est souvent pratique d’utiliser l’adresse d’une variable pour l’affecter à un pointeur. Celle-ci s’obtient...
Définir et utiliser une union
Problème
Vous souhaitez utiliser la même variable pour stocker des valeurs de types différents.
Solution
Une union permet de définir plusieurs types pour une variable. Pour stocker des valeurs de types différents au même endroit, utilisez une union.
Discussion
Une union se déclare et s’utilise comme une structure. Par contre, les différents champs de l’union ne sont pas consécutifs mais correspondent au même emplacement en mémoire. Vous ne pouvez donc mettre qu’une valeur à la fois dans une union.
Le C est un langage typé. Une union permet donc de passer outre le type d’une variable en ayant indiqué au préalable les différents types possibles par l’intermédiaire de cette union. Dans un style de programmation visant la stabilité du programme et la lisibilité du code, une union est donc un élément perturbant. Aussi, vous n’utiliserez pas d’unions à moins d’en avoir réellement besoin et de savoir ce que vous faites.
Une union peut être utile lorsque vous devez pouvoir accéder aussi bien à une donnée qu’à une partie de cette donnée. Par exemple, pour convertir un masque de sous-réseau sous forme d’entiers en chaîne de caractères hexadécimale et inversement...
Définir et utiliser une fonction
Problème
Vous voulez isoler une partie de votre code et la nommer afin de pouvoir l’exécuter en l’appelant par son nom.
Solution
Définissez une fonction selon la syntaxe suivante :
type nom (type1 arg1, type2 arg2, ..., typen argn) {
instruction1; instruction2;
/* ... */
instructionn;
return (valeur de retour);
}
Pour appeler la fonction ainsi définie, indiquez son nom, suivi des arguments entre parenthèses.
Discussion
Une fonction a généralement pour but d’isoler une partie de code qui effectue un groupe d’opérations liées. Elle permet de rassembler ces opérations dans un bloc d’instructions et de lui donner un nom. Exécuter ces opérations revient alors à exécuter la fonction.
La syntaxe de la définition d’une fonction comprend plusieurs notions :
-
son nom : c’est ainsi que vous appellerez le bloc d’instructions ;
-
son type : une fonction peut renvoyer une valeur conformément à son type. Une fonction qui ne renvoie pas de valeur est de type void ;
-
ses arguments : vous pouvez appeler la fonction en lui passant des paramètres. Le nom qu’ils porteront à l’intérieur de la fonction est défini entre parenthèse, ainsi que leur type ;
-
son contenu : il s’agit du bloc d’instructions ;
-
sa valeur de retour : elle est indiquée à l’instruction return.
L’exemple ci-dessous présente un cas simple de fonction (par ailleurs inutile puisque l’opérateur + fait mieux) :
int
addition_entiere (int a, int b) ...
Passer des paramètres à une fonction
Problème
Vous voulez passer des paramètres à une fonction et vous avez besoin que la fonction en modifie certains.
Solution
Passez les paramètres par référence en indiquant non pas les variables mais leur adresse.
Discussion
En passant l’adresse d’une variable à une fonction, vous ne transmettez pas sa valeur mais donnez à la fonction un accès complet à son contenu. Elle est alors en mesure de modifier une telle variable. L’exemple suivant montre comment renvoyer à la fois le résultat d’une division entière et le reste :
int
division_entiere (const int a, const int b, int *r)
{
int q = a / b;
*r = a % b;
return (q);
}
/* ... */
int quotient;
int reste;
quotient = division_entiere (10, 4, &reste);
printf ("4 * %d + %d = 10\n", quotient, reste);
Dans cet exemple, le quotient de la division est renvoyé comme d’habitude. Comme il n’est possible de retourner qu’une seule valeur, nous profitons du passage par référence du paramètre reste pour le modifier dans la fonction. Cela nous oblige à travailler avec des pointeurs à l’intérieur de la fonction.
L’utilisation des pointeurs doit vous inciter à prêter une attention toute particulière...
Utiliser une fonction de façon récursive
Problème
Vous programmez une fonction qui a besoin de faire appel à elle-même.
Solution
Dans une fonction, il n’y a aucune différence syntaxique entre un appel à elle-même ou à une autre. La particularité d’une fonction récursive, autrement dit qui s’appelle elle-même, est qu’elle doit contenir une condition dite d’arrêt, qui arrête la récursion.
Discussion
Une fonction qui s’appelle elle-même, sans la précaution d’inclure une condition d’arrêt, est une boucle implicite et infinie. La fonction s’appelle alors elle-même en boucle jusqu’à épuisement des ressources. C’est ce qu’il faut éviter.
Une fonction récursive doit, à l’endroit où elle s’appelle, être codée d’une façon semblable à celle-ci :
void
fonction_recursive (int param, /* ... */)
{
/* ... */
if (!condition_arret)
{
fonction_recursive (/* ... */);
}
/* ... */
}
Le calcul du PGCD (plus grand commun diviseur) de deux nombres par la méthode d’Euclide se programme très bien avec la récursivité. Cette méthode consiste, pour deux entiers a et b, à dire que si b est nul...
Écrire un fichier d’en-têtes
Problème
Des variables ou fonctions de votre programme sont définies dans un fichier source autre que celui qui y fait appel. Vous voulez écrire un fichier d’en-têtes à inclure dans les fichiers qui font appel à ces fonctions.
Solution
Créez un fichier correspondant aux fonctions et variables définies dans le fichier source. Donnez-lui de préférence le même nom que le fichier source, mais changez l’extension en .h. Placez-y les prototypes des fonctions et indiquez les variables globales.
Discussion
Un fichier d’en-têtes est un fichier à inclure dans le code source avec la directive #include. Voici deux exemples :
#include <en_tetes1.h>
#include "en_tetes2.h"
La différence entre ces deux syntaxes se situe dans les chemins de recherche de ce fichier d’en-têtes :
-
<fichier.h> est recherché dans les chemins standards du compilateur. Il s’agit généralement de /usr/include sur Unix, mais aussi les chemins indiqués en paramètre du compilateur (avec l’option -Ichemin pour gcc ; les chemins indiqués avec -I sont prioritaires par rapport à ceux intégrés au compilateur).
-
"fichier.h" est recherché dans le répertoire relatif à celui contenant le fichier source qui l’inclut.
Par conséquent, indiquer l’option -I. au compilateur permet d’utiliser...
Compiler une section de code en fonction d’une condition
Problème
Vous ne souhaitez compiler une section de code que si une condition est définie, comme par exemple du code spécifique à un système d’exploitation.
Solution
Utilisez les directives de compilation conditionnelle comme #if, #ifdef ou #ifndef.
Discussion
De la même façon que vous disposez de l’instruction if en C, le préprocesseur vous permet d’utiliser #if. Cette directive est à l’attention du préprocesseur et non du compilateur. En effet, si la condition est remplie, le préprocesseur recopie ce qui suit dans le code à compiler. Dans le cas inverse, le préprocesseur ignore la suite jusqu’au #endif et le compilateur n’a tout simplement pas connaissance de ce code. La différence fondamentale entre la directive #if et l’instruction if est donc que la première évalue la condition à la compilation (donc une bonne fois pour toutes) et la seconde à l’exécution.
La syntaxe de #if est la suivante :
#if condition
/* Lignes à prendre en compte à la compilation
* si la condition est remplie
* et à effacer sinon.
*/
#endif
Comme en C avec if, vous disposez aussi de la directive #else.
La condition est à écrire avec ce que le préprocesseur est en mesure de comprendre....
Exécuter une fonction avant la fin du programme
Problème
Lorsque votre programme prend fin, vous voudriez qu’une fonction soit exécutée.
Solution
Utilisez la fonction atexit() pour indiquer la fonction à utiliser lorsque votre programme prend fin (à la fin de la fonction main() ou à l’exécution d’exit()).
Dans certains cas, vous préférerez la nouvelles fonction de la norme C11 at_quick_exit() lors d’un appel à quick_exit() pour mettre fin au programme de façon accélérée.
Discussion
Un programme prend fin normalement en fin d’exécution de main() ou lors d’un appel à exit(). Il existe au moins deux autres façons pour un programme de s’arrêter. Citons le cas du traitement d’un signal (voyez le chapitre "Signaux" à ce sujet) dont le résultat serait la fin du programme. La fonction _exit() permet aussi une sortie rapide du programme. Si votre compilateur respecte la norme C11, quick_exit() est une troisième façon de sortir rapidement du programme : cette fonction appelle directement _exit().
La différence entre ces diverses fonctions se situe dans la possibilité ou non de lancer des fonctions programmées pour la fin du programme.
La fonction atexit() permet d’enregistrer des fonctions à exécuter lors de l’appel...