Paul Schuhmacher
Septembre 2026
Module: Langage C (T1)
Ce tutoriel est en partie inspiré du premier chapitre du livre The C programming language, The White Book ou K&R, écrit par les deux géants Dennis Ritchie, créateur du langage et Brian Kernighan, publié chez Prentice Hall, 1978.
main.c et y
copier/coller le code source C suivant :#include <stdio.h>
int main(){
printf("hello, world\n");
return 0;
}gcc (GNU/Linux). Cela produit un
exécutable a.out ou .exe (sur Windows) :gcc main.c
./aout\n” et
observer le résultat.printf("hello, world\n);printf("hello, world\n"). En C,
toute instruction se termine par un point virgule.printf contient
"\c" ?--output ou -ogcc main.c -o myprogram
./myprogramNous allons écrire un programme qui affiche une table de conversion entre degré Celsius et degrés Fahrenheit. Voici le résultat que l’on doit obtenir :
0 -17
20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 714
180 82
200 93
220 104
240 115
260 126
280 137
300 148
Noter le formatage de la sortie et l’alignement des colonnes
Voici un programme qui affiche cette table :
#include <stdio.h>
/*Fournit la table de conversion degrés Fahrenheit/Celsius*/
int main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
fahr = lower;
while (fahr <= upper)
{
celsius = (fahr - 32) * 5 / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
return 0;
}conversion.c. Compiler le programme et
produire un executable appelé conversion.
Étudier le programme. Essayer de le
modifier, testez-le.Il y a plusieurs problèmes avec ce programme. Nous allons les corriger.
printf en écrivant la première colonne sur 3
caractères et la deuxième colonne sur 6
caractères. Pour cela, utiliser le spécificateur de taille
%xd où x indique le nombre de caractères sur
lequel écrire un entier.int) par des types flottants (float). Quel
effet a cette modification sur la sortie produite ?%xd par
%x.yf, où %f est le spécificateur pour les
nombres flottants. Que contrôlent les paramètres x et
y dans ce spécificateur ? Faire des tests pour y répondre
:printf("%f\t%f\n", fahr, celsius); Fahrenheit Celsius
0 -27.65
20 -21.48
40 -15.31Noter la justification à droite des headers. Indice: compter le nombre de caractères dans chaque header et ajustez le nombre de caractères sur lequel imprimer chaque valeur de température.
%f uniquement (retirer le tab \t)
: 0 -17.8
20 -6.7
40 4.4
60 15.6
80 26.7
100 37.8
120 48.9
140 60.0
160 71.1while par une
boucle for. Voici la syntaxe de la boucle for
en C :for ([expression1]; [expression2]; [expression3])
{
//Instructions
}où :
expression1 : (initialisation) : Elle est
exécutée une seule fois, au début de la boucle. Elle
sert souvent à initialiser une variable de contrôle (comme un compteur).
Par exemple : int i = 0;expression2 : (condition) : Cette
expression est évaluée avant chaque itération. Tant qu’elle est
vraie, la boucle continue de s’exécuter. Si elle est
fausse, la boucle s’arrête. Par exemple : i <= 10;expression3 : (incrémentation ou mise à jour)
: Elle est exécutée à la fin de chaque itération, après
l’exécution du bloc de code de la boucle. Elle permet de mettre à jour
la variable de contrôle. Par exemple : i = i + 1.#define NAME replacement listCette instruction spéciale, démarrant avec un # est une
instruction pour le préprocesseur. Le préprocesseur est un
programme qui traite le code source avant la phase de
compilation. Il effectue des manipulations textuelles simples sur le
fichier source, sans vérifier la syntaxe C ni le sens du code.
Le préprocesseur va remplacer chaque occurrence de NAME par
sa valeur de remplacement (search and replace de texte !). Par
exemple :
#include <stdio.h>
//Déclaration d'une constante symbolique MAX
#define MAX 100
int main(){
printf("MAX = %d\n", MAX);
return 0;
}Par convention, on écrit les constantes symboliques en majuscules pour les distinguer des noms de variables écrites usuellement en minuscules.
Le code après passage du préprocesseur et avant la compilation va ressembler à cela :
#include <stdio.h>
int main(){
printf("MAX = %d\n", 100);
return 0;
}L’exécution du programme produit alors la sortie suivante :
MAX = 100C’est une mauvaise pratique de “hard coder” des valeurs dans le programme (nombres “magiques”), car ces valeurs, esseulées, ne donnent aucune information à une personne relisant le programme (que signifient-elles ? quel contexte ?). Et s’il on souhaite les changer, il faut penser à le faire partout dans le code. Une solution est de donner un nom à ces nombres, via les constantes symboliques.
parrot.c. Ce programme va copier sur la sortie les
caractères qu’il reçoit en entrée tant qu’il ne reçoit pas d’instruction
de terminer la saisie. Voici le code C de ce programme :#include <stdio.h>
int main()
{
int c;
while ((c = getchar()) != EOF)
{
putchar(c);
}
return 0;
}Compiler, exécuter et tester le code.
Ce programme répète tout ce que vous écrivez.
Analyse du code :
!= signifie “n’est pas égal à”, “est
différent de”getchar() est une fonction (fournie par
stdio.h) qui permet de lire le caractère à la position
courante du flux d’entrée standard, appelé stdin. Un
flux (ou stream) est une abstraction
logicielle gérant le transfert séquentiel de données via une
mémoire tampon temporaire. stdin (pour Standard Input, ou
entrée standard) est le flux d’entrée mis
automatiquement à disposition d’un programme lorsqu’il démarre. En
général, ce flux est relié aux saisies du clavier.putchar() est une fonction (fournie par
stdio.h) qui permet d’écrire un caractère sur le flux
de sortie standard, appelé stdout. stdout (pour Standard
Output, ou sortie standard) est le flux de sortie
standard par défaut mis à disposition d’un programme lorsqu’il
démarre. En général, ce flux est relié au terminalEOF est une constante symbolique fournie par
la bibliothèque standard qui signifie “End Of File”. Ce n’est
pas un caractère mais un entier négatif (généralement -1). Cette valeur
particulière est utilisée pour indiquer la fin de la saisie.
Pour la déclencher, tapez Ctr + D sur Unix ou
Ctr + Z suivie de Entrée sur Windows.En C, la fonction getchar() sert à lire un seul
caractère sur le flux d’entrée standard
(stdin, le clavier par défaut). Elle renvoie le code ASCII
du caractère lu (promu en int) ou la constante
EOF (End Of File, valant généralement
-1) en cas d’erreur ou de fin de fichier.
Ces informations se trouvent dans la documentation de la librairie
standard, accessible via le programme Unix de manuel man
:
# Accéder au manuel de getchar
man getchargetchar() ne lit pas la touche immédiatement au
moment où l’on appuie dessus. Le terminal utilise un tampon de
ligne (line-buffered) par défaut.
Entrée (ce qui
permet de corriger sa saisie avec la touche Effacer par exemple !).Entrée le terminal
vide son buffer (flush) et envoie alors
l’intégralité de la ligne (et le caractère de saut de ligne
"\n" !) au stream stdin.getchar() extrait et
consomme le premier caractère disponible dans le flux
stdin. Les caractères suivants y restent stockés en attente
des prochains appels à getchar(). Lorsque le tampon de
stdin reçoit le signal fin de saisie (0 byte lu)
via Ctl + D, cela indique une fin de flux
(il n’y a plus rien à lire !). getchar retourne alors
EOF.Voici un schéma récapitulant ce qu’il se passe à l’utilisation du programme :
[ SOURCE ] [ BUFFER du TERMINAL ] [ BUFFER du stream STDIN ] [ PROGRAMME C ]
+---------+ +---------------------+ +---------------------------+ +-------------+
| Clavier | --[1]--> | 'h','e','l','l','o' | | | | |
+---------+ +---------------------+ | | | |
| | | | |
| [2] Touche "Entrée" | | | |
v | | | |
+------------------+ | | | |
| (Flush) | --[3]--> | 'h','e','l','l','o' '\n' | | |
+------------------+ +---------------------------+ | |
^ | |
| Curseur de lecture | |
| | |
+-----[4] getchar() ---------------->| c = 'h' |
| |
| [5] (Avance le curseur) | |
v | |
+---------------------------+ | |
| 'e','l','l','o' '\n' | | |
+---------------------------+ +-------------+Lorsque l’on appuie sur Ctrl + D, le terminal transmet
immédiatement le contenu de son tampon à stdin :
EOF
et getchar() retourne simplement la constante
EOF (-1) indiquant au programme la fin de la lecture."foo"
sans appuyer sur Entrée), le tampon du terminal se vide et
envoie les octets ‘f’, ‘o’ et ‘o’ à stdin (sans ajouter de
\n). getchar lit ces 3 caractères, mais
l’opération a renvoyé 3 octets (et non 0) : ce n’est donc pas
une fin de flux. Le flux stdin est vide mais reste
ouvert. getchar se met donc en attente de la
suite. En appuyant une seconde fois sur Ctrl + D
(maintenant que le tampon du terminal est vide !), le terminal transmet
0 octet à stdin et déclenche l’EOF (retour au
premier cas)En C, un flux ou stream est une abstraction logicielle qui représente une source ou une destination séquentielle de données (bytes ou octets). Il sert d’intermédiaire entre le programme C et les périphériques physiques (clavier, écran, fichier sur disque, socket réseau).
Un stream est caractérisé par :
EOF) et l’indicateur d’erreur. Ces états permettent au
consommateur du flux (le programme C ici) de savoir si la fin des
données est atteinte ou si une défaillance s’est produite lors du
transfert.L’intérêt principal d’un flux est qu’il n’est pas nécessaire de charger l’intégralité des données en mémoire pour les traiter. Les données sont lues, transformées et transmises au fur et à mesure. Cela permet de traiter des flux de données en continu (data streams) qui n’ont pas de taille définie à l’avance ou qui ne s’arrêtent jamais : la saisie clavier d’un utilisateur, les logs d’un serveur en temps réel ou un flux réseau (ex: streaming vidéo).
Par exemple, un programme C qui traite un fichier de 100 Go avec
getchar() consomme exactement la même quantité de
mémoire RAM (quelques octets pour le tampon) que s’il traitait un
fichier de 10 octets !
Flux stdin Source
[FLAGS]
Curseur
|
v
---------------------------------------
getchar() <---['h']['e']['l']['l']['o']['\n'][][][][] <---- clavier (par défaut)
---------------------------------------
Un flux est comme un tuyau : c’est un canal dans lequel les données s’écoulent en file indienne (séquentiellement) : chaque donnée arrive l’une après l’autre. Le tuyau peut stocker une petite quantité de données (buffer). Ce tuyau est connecté à une source de données. Le flux
stdinest branché par défaut sur le clavier. Il est consommé pargetchar(), une donnée après l’autre. Quand le tuyau est vide,getchar()le constate et renvoieEOF.
Un flux permet de créer une abstraction sur l’origine des données. Il est possible de rediriger les flux c’est à dire de l’associer à une autre source ou destination. Par exemple, le flux stdin est associé aux saisies clavier par défaut. On peut rediriger stdin : au lieu d’être associé au clavier, on peut l’associer à un fichier !
stdin n’est rien d’autre qu’un canal ouvert automatiquement et accessible à chaque programme. Ce qui se trouve à l’autre bout du canal (source des données) peut changer !
Créer le fichier shakespeare.txt
contenant le sonnet suivant :
From fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as the riper should by time decease,
His tender heir might bear his memory:
But thou, contracted to thine own bright eyes,
Feed'st thy light's flame with self-substantial fuel,
Making a famine where abundance lies,
Thy self thy foe, to thy sweet self too cruel:
Thou that art now the world's fresh ornament,
And only herald to the gaudy spring,
Within thine own bud buriest thy content,
And tender churl mak'st waste in niggarding:
Pity the world, or else this glutton be,
To eat the world's due, by the grave and thee.
À l’exécution d’un programme, il est possible de rediriger
le flux d’entrée standard avec le symbole <. Par exemple
:
cc parrot.c -o parrot
./parrot < shakespeare.txtIci on dit “Associe le fichier shakespeare.txt au
flux d’entrée standard” (et non le clavier par défaut!). Le
programme fonctionne de la même manière et va lire le contenu du fichier
! getchar() lit un flux séquentiellement d’octets (bytes)
sans se soucier de la source de données !
Flux stdin Source
Curseur
| ------`shakespeare.txt`
v |
--------------------------------------- |
getchar() <---['F']['r']['o']['m'][' ']['f'][..][][][] <--- clavier (défaut)
---------------------------------------
Remarquez comment les abstractions fournies par le système Unix et le C permettent d’écrire un code totalement indépendant de la source des données. La fonction
getchar()n’a pas besoin de savoir si les caractères proviennent du clavier, d’un fichier texte ou d’un réseau : elle lit simplement un flux d’octets.
On vient de rediriger le flux d’entrée standard (stdin) mais
on peut également rediriger le flux de sortie standard (stdout)
! Par défaut, ce flux a pour destination le terminal. C’est le flux
qu’utilise printf. On peut le rediriger vers un fichier
avec le symbole >. Ainsi :
./parrot < shakespeare.txt > copydit “Associe le fichier copy au flux de sortie standard
stdout”. Le résultat est la copie le contenu du fichier
shakespeare.txt dans le fichier copy.
Inspecter le contenu de copy :
cat copyAvec les redirections de flux Unix, notre programme
parrot, qui semblait un peu inutile, permet à présent de
dupliquer un fichier !
Nous verrons plus tard comment rediriger les flux directement en C.
c = getchar() :#include <stdio.h>
int main()
{
int c;
/*while ((c = getchar()) != EOF)*/
while (c = getchar() != EOF)
{
putchar(c);
}
return 0;
}Dans l’expression c = getchar() != EOF, l’ordre
d’évaluation dépend de la priorité, appelée précédence,
des opérateurs impliqués. Chaque opérateur a une précédence
différente par rapport aux autres. L’opérateur de comparaison
!= a une priorité supérieure à l’opérateur d’affectation
=. Aussi, l’expression c = getchar() != EOF
est évaluée comme suit c = (getchar() != EOF). Les
parenthèses forcent l’ordre d’évaluation, ce qui est entre parenthèses
est évalué d’abord.
putchar(c) par
printf("%d", c). Expliquer le résultat
obtenu.En C, une valeur vraie (
true) est représentée par une valeur entière différente de0(1, 2, etc.), une valeur fausse (false) est représentée exclusivement par la valeur 0.
Corriger l’erreur en remettant les parenthèses
autour de c = getchar(). Vérifier le bon
comportement du programme.
#include <stdio.h>
int main()
{
int nc = 0;
while (getchar() != EOF)
nc++;
printf("Nombre de caractères : %d\n", nc);
return 0;
}for à la place de while et un
double à la place d’un int pour la variable
nc.'\n'.e fourni en entrée via le clavier.e
présent dans le sonnet présent dans le fichier
shakespeare.txt crée précédemment.Dans un nouveau fichier math.c, écrire un
programme qui demande à l’utilisateur de fournir un nombre et
lui affiche la racine carrée de de ce nombre. Pour calculer la racine
carrée, vous pouvez utiliser la fonction sqrt(x) fournie
par la standard library.
Pour accéder à cette fonction, inclure le header
math.h avec #include <math.h> et
compiler avec l’option -lm :
gcc math.c -lm.
#include <math.h>
#include <stdio.h>
int main(){
float x = sqrt(2);
printf("%f\n", x);
}et compiler ainsi :
gcc math.c -lmL’édition des liens vers le code des fonctions mathématiques (comme
sqrt) définies dans le headermath.hn’est pas fait automatiquement par le compilateur (contrairement àstdio.hetstdlib.h). Pour compiler, vous devez explicitement faire l’édition des liens avec l’option-lm. Nous y reviendrons plus tard.
Si vous avez terminé ou êtes en avance, continuer votre découverte du C en consultant les ressources suivantes :