2021-08-23 18:54:19 +11:00
|
|
|
|
\chapter{Le langage C}
|
2021-07-31 08:45:42 +11:00
|
|
|
|
\label{C}\index{C}
|
|
|
|
|
|
|
|
|
|
Un chapitre un peu particulier, puisque c'est le début d'une
|
|
|
|
|
initiation au langage C pour les non-codeurs qui souhaitent
|
|
|
|
|
rentrer dans le sujet à la dure.
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Certaines des explications qui vont suivre ne sont pas très
|
|
|
|
|
rigoureuses, mais montrent bien les principes généraux et
|
|
|
|
|
les erreurs classiques. La futilité des exemples est assumée.
|
|
|
|
|
|
|
|
|
|
Les détails nécessaires seront présentés dans la suite.
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
|
|
|
|
\section{Hello World}
|
|
|
|
|
|
|
|
|
|
Hop, on y va...
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Le tout début de la vie d'un programme écrit en C est l'appel par
|
|
|
|
|
le \textsl{runtime}\footnote{\texttt{crt0.s}} d'une fonction
|
|
|
|
|
nommée \texttt{main} qui recevra deux paramètres\footnote{%
|
|
|
|
|
En fait trois, mais restons simple.}
|
2021-07-31 08:45:42 +11:00
|
|
|
|
dont l'utilisation est décrite un peu plus bas.
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Ces paramètres sont fournis par des mécanismes planquées
|
|
|
|
|
dans la soute du système d'exploitation, et n'ont pas
|
|
|
|
|
d'importance pour le moment..
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
|
|
|
|
\lstinputlisting[language=c]{code/hello.c}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Un fois passé l'entrée, nous sommes dans la partie active.
|
|
|
|
|
Nous appelons à ce moment une fonction de la bibliothèque
|
|
|
|
|
standard : \texttt{puts}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
qui a pour but d'afficher sur l'écran le texte
|
|
|
|
|
passé en paramètre.
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Voila, c'est fait. Dans les règles de l'art.
|
|
|
|
|
Nous savons dire « bonjour » au monde.
|
|
|
|
|
Point suivant~: dire bonjour à quelqu'un d'autre. Et pour
|
2021-07-31 08:45:42 +11:00
|
|
|
|
cela il nous fait récupérer un argument depuis la ligne
|
|
|
|
|
de commande.
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
|
|
|
|
\section{Arguments}
|
|
|
|
|
|
|
|
|
|
Expliquons maintenant les deux paramètres
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\texttt{argc} et \texttt{argv} du point d'entrée du programme
|
|
|
|
|
(la fonction main).
|
2021-07-31 08:45:42 +11:00
|
|
|
|
Le premier est le nombre de "mots" détectés par l'interpréteur
|
|
|
|
|
de commande qui va lancer votre proggy, et le second est
|
2021-08-23 18:54:19 +11:00
|
|
|
|
un tableau de chaines de caractères contenant ces différents mots.
|
|
|
|
|
|
|
|
|
|
Ce petit bout de code va nous afficher tout ça~:
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
|
|
|
|
\lstinputlisting[language=c]{code/arguments.c}
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Et voici un exemple d'exécution depuis un shell~:\index{shell}
|
|
|
|
|
|
2021-07-31 08:45:42 +11:00
|
|
|
|
\begin{verbatim}
|
|
|
|
|
$ ./arguments un deux "trois quatre"
|
2021-08-23 18:54:19 +11:00
|
|
|
|
0 ./arguments
|
|
|
|
|
1 un
|
|
|
|
|
2 deux
|
|
|
|
|
3 trois quatre
|
2021-07-31 08:45:42 +11:00
|
|
|
|
$
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Nous constatons que la première valeur affichée est en fait
|
|
|
|
|
le nom de notre programme, ou plutôt le chemin vers le fichier
|
|
|
|
|
exécutable, et surtout que son indice est 0, ce qui semble
|
|
|
|
|
logique\footnote{Rez-de-chaussée, premier étage, toussa\dots}.
|
|
|
|
|
En C, les tableaux commencent toujours à l'indice 0.
|
|
|
|
|
|
|
|
|
|
Pour le traitement des options, il faut sauter à
|
|
|
|
|
la page \pageref{getopt}.
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\section{Les variables}
|
|
|
|
|
|
|
|
|
|
En gros, une variable du C est une zone de mémoire destinée
|
|
|
|
|
à contenir une valeur.
|
|
|
|
|
Une variable peut être caractérisée par trois choses~:
|
|
|
|
|
son nom, son type, et sa portée.
|
|
|
|
|
|
|
|
|
|
\textbf{Le nom} : Il doit commencer par une
|
|
|
|
|
lettre\footnote{To be ASCII or not to be ?} majuscule ou minuscule,
|
|
|
|
|
laquelle peut être suivie d'un nombre suffisant de lettres, de chiffres
|
|
|
|
|
et du caractère 'souligné'. La différence de casse est signifiante.
|
|
|
|
|
|
|
|
|
|
\textbf{Le type} : C'est une désignation du genre d'information
|
|
|
|
|
que l'on peut stocker dans une variable.
|
|
|
|
|
|
|
|
|
|
\textbf{La portée} :
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\section{Les fonctions}
|
|
|
|
|
|
|
|
|
|
Nous avons vu brièvement dans la première section de ce chapitre
|
|
|
|
|
la fonction \texttt{main} et ses deux paramètres.
|
|
|
|
|
Il est temps de préciser les détails.
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Une fonction a un unique point d'entrée\footnote{Qui a déja joué
|
|
|
|
|
avec le ENTRY du Fortran ?}, avec une éventuelle liste
|
|
|
|
|
de paramètres,
|
|
|
|
|
et une tout aussi éventuelel unique valeur de retour.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
XXX\index{XXX}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Entrées / Sorties}
|
|
|
|
|
\index{stdin} \index{stdout} \index{stderr}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Il est évident qu'un logiciel qui n'a aucun moyen de communiquer
|
|
|
|
|
avec le monde extérieur a bien moins d'utilité qu'un bon ed.
|
|
|
|
|
Dans le schéma «~classique~», ces échanges ont lieu par
|
|
|
|
|
le truchement d'un terminal.
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\subsection{Les trois flux canoniques}
|
|
|
|
|
|
|
|
|
|
\texttt{stdin}, \texttt{stdout} et \texttt{stderr}\dots
|
|
|
|
|
|
|
|
|
|
Par défaut, au lancement du programme, ces trois canaux
|
|
|
|
|
d'entrée/sortie sont pré-connectés, et donc directement
|
|
|
|
|
utilisables.
|
|
|
|
|
Le premier (\texttt{stdin}), l'entrée standard, est connecté
|
|
|
|
|
au clavier du consoliste\footnote{De quel roman est tiré ce terme ?},
|
|
|
|
|
le second (\texttt{stdout}), la sortie standard, permet d'afficher
|
|
|
|
|
les résultats du programme sur l'écran,
|
|
|
|
|
et le troisème (), la sortie d'erreur, permet d'afficher
|
|
|
|
|
les éventuels messages d'erreur.
|
|
|
|
|
La différence entre \textsl{out} et \textsl{err} est expliquée
|
|
|
|
|
plus loin.
|
|
|
|
|
|
|
|
|
|
\subsection{IO de base}\index{getchar}\index{putchar}
|
|
|
|
|
|
|
|
|
|
Les fonctions \texttt{getchar} et \texttt{putchar} sont les plus
|
|
|
|
|
simples.
|
|
|
|
|
Avec elles, on peut lire un caractère depuis \texttt{stdin},
|
|
|
|
|
et afficher un caractère dans \texttt{stdout}. Tout cela semble
|
|
|
|
|
très bien, mais la fonction de lecture est piégeuse
|
|
|
|
|
"\textsc{It's a trap}", comme nous allons bientôt le voir.
|
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
Vous devez logiquement penser que si nous allons lire un caractère
|
|
|
|
|
depuis l'entrée du programme, nous pouvons utiliser une variable de
|
|
|
|
|
type \texttt{char}, puisque ce type est prévu pour stocker un
|
|
|
|
|
caractère. Seulement, pour indiquer l'abscence de caractère,
|
|
|
|
|
la fin du fichier, il nous faut une valeur \textsl{out of band}
|
|
|
|
|
et donc getchar renvoit en fait un \texttt{int} dont une valeur
|
|
|
|
|
particulière sera nommée \texttt{EOF}, \textsl{end of file}.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
while (EOF != (foo=getchar())) { ....
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Voilà un piège éliminé, et vous trouverez un exemple complet
|
|
|
|
|
un peu plus loin.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\subsection{Écrire : \texttt{printf}}
|
|
|
|
|
\index{printf}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
La fonction \texttt{printf} permet d'afficher sur la sortie
|
|
|
|
|
standard le contenu d'une (ou plusieurs)
|
|
|
|
|
variable sous diverses présentations,
|
2023-07-15 18:55:13 +11:00
|
|
|
|
qui sont contrôlables par un tout petit
|
|
|
|
|
DSL\footnote{\textsl{domain specific language}}\index{DSL}~:
|
2023-04-03 08:32:29 +11:00
|
|
|
|
la chaine de format.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
int bar, foo = 42;
|
|
|
|
|
bar = printf ("foo = %d\n", foo);
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
La première ligne déclare deux variables dont une (foo) est
|
|
|
|
|
initialisée à une valeur connue. La seconde ligne appelle
|
|
|
|
|
la fonction '\texttt{printf}' avec les paramêtres appropriés et
|
|
|
|
|
conserve la valeur de retour de celle-ci.
|
|
|
|
|
|
|
|
|
|
Le premier de ces paramêtres, \texttt{"foo = \%d\textbackslash{}n"},
|
2023-04-03 08:32:29 +11:00
|
|
|
|
est appelé \emph{chaine de format}. C'est une chaine de caractères,
|
2021-08-23 18:54:19 +11:00
|
|
|
|
que l'on peut classer en trois catégories~:
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
\begin{itemize} % ------------
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
\item{\textsl{caractères crus} : ils sont directements poussés vers
|
|
|
|
|
la sortie. La plupart d'entre eux donnent le résultat attendu.}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
\item{\textsl{codes de format} : les fragments qui commencent par le
|
|
|
|
|
caractère '\%'. Ils servent à contrôler l'apparence de ce qui va
|
2023-04-03 08:32:29 +11:00
|
|
|
|
suivre.
|
|
|
|
|
Par exemple \texttt{\%x} va afficher un \texttt{int} en hexadécimal}.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
\item{\textsl{échappements} : les caractères précédés d'un
|
|
|
|
|
'\textbackslash' ou \textsl{backslash}\footnote{barre-penchée-du-8}
|
|
|
|
|
permettent d'inclure dans la chaine de format des caractères
|
|
|
|
|
non imprimables.
|
|
|
|
|
Par exemple, \texttt{'\textbackslash{}n'} va générer le caractère
|
|
|
|
|
\textsl{newline} qui marque la fin d'une ligne de texte.
|
|
|
|
|
}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
\end{itemize} % ------------
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2021-09-28 01:09:50 +11:00
|
|
|
|
Quand à la valeur de retour, elle contient le nombre de caractères
|
2023-04-03 08:32:29 +11:00
|
|
|
|
effectivement écrits. Ce nombre peut être différent du nombre attendu
|
|
|
|
|
en cas d'erreur, par exemple si le disque est saturé.
|
2021-09-28 01:09:50 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Ce nombre, que beaucoup de gens négligent, peut être utilisé pour un
|
|
|
|
|
affichage de multiples données en limitant la longueur des lignes~:
|
2021-09-28 01:09:50 +11:00
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
int foo, nbre = 0;
|
|
|
|
|
for (foo=0; foo<1000; foo++) {
|
|
|
|
|
nbre += printf("%d ", foo);
|
|
|
|
|
if (nbre > 62) {
|
|
|
|
|
putchar('\n'); nbre = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2023-07-15 18:55:13 +11:00
|
|
|
|
% ---------------------------------------------------------
|
|
|
|
|
%
|
|
|
|
|
% voir aussi ~/code/network/README.md
|
|
|
|
|
%
|
|
|
|
|
\subsection{Lire : \texttt{fgets}}
|
|
|
|
|
\index{fgets}
|
|
|
|
|
|
|
|
|
|
Cette fonction a pour but de lire une ligne de texte depuis
|
|
|
|
|
une entrée (stdin, fichier, socket\index{socket}...)
|
|
|
|
|
et de la ranger en mémoire.
|
|
|
|
|
Mais commençons par lire la documentation\index{RTFM} de cette fonction,
|
|
|
|
|
en nous concentrant sur ces deux passages, avant de passer à
|
|
|
|
|
un exemple\footnote{encore en phase de méditation}.
|
|
|
|
|
|
|
|
|
|
Voici deux extraits de ce que nous affirme la page du manuel~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
DESCRIPTION
|
|
|
|
|
fgets() reads in at most one less than size characters from stream and
|
|
|
|
|
stores them into the buffer pointed to by s. Reading stops after an
|
|
|
|
|
EOF or a newline. If a newline is read, it is stored into the buffer.
|
|
|
|
|
A terminating null byte ('\0') is stored after the last character in
|
|
|
|
|
the buffer.
|
|
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
|
fgets() returns s on success, and NULL on error or when end of file oc‐
|
|
|
|
|
curs while no characters have been read.
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Prenez le temps d'y réfléchir, et essayons un cas simple d'utilisation
|
|
|
|
|
pour examiner le comportement de cette fonction. Nous allons
|
|
|
|
|
boucler sur la lecture d'une ligne depuis l'entrée standard dans
|
|
|
|
|
un buffer d'une taille fixée, puis afficher le nombre de caractères
|
|
|
|
|
que nous avons reçus.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[language=c]{code/C/fgets-simple.c}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
|
|
|
|
|
% ---------------------------------------------------------
|
2023-07-15 18:55:13 +11:00
|
|
|
|
%
|
|
|
|
|
%
|
2023-04-03 08:32:29 +11:00
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\subsection{Lire : \texttt{scanf}}
|
|
|
|
|
\index{scanf}
|
|
|
|
|
|
|
|
|
|
Avez-vous bien révisé la section qui cause des pointeurs ?
|
2021-09-27 09:57:38 +11:00
|
|
|
|
Êtes-vous prèt à d'étranges comportements ?
|
2023-07-15 18:55:13 +11:00
|
|
|
|
À ne plus savoir où peut être la tête de lecture ?
|
|
|
|
|
Alors les subtilités de \texttt{scanf} vont vous plaire.
|
2021-09-27 09:57:38 +11:00
|
|
|
|
|
|
|
|
|
\textit{To be continued...}
|
|
|
|
|
|
2023-07-15 18:55:13 +11:00
|
|
|
|
% ---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
2021-09-27 09:57:38 +11:00
|
|
|
|
\subsection{Les fichiers}\index{fopen}\index{fclose}
|
|
|
|
|
|
|
|
|
|
Lire et écrire depuis les flux gérés par le système, c'est bien, mais
|
|
|
|
|
c'est mieux de pouvoir faire la même chose depuis ou vers un fichier
|
|
|
|
|
enregistré. Il nous faut un moyen pour se 'connecter' à un ficher, et
|
|
|
|
|
y balancer des trucs à grand coup de printf.
|
|
|
|
|
|
|
|
|
|
Ce mécanisme passe par la fonction \texttt{fopen} et \texttt{fclose}.
|
|
|
|
|
La première va nous renvoyer (si tout se passe bien) un pointeur
|
|
|
|
|
sur une structure opaque de type \texttt{FILE *} qui pourra être
|
|
|
|
|
utilisé dans la suite des opérations. Voici son prototype~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
FILE *fopen(const char *pathname, const char *mode);
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Le premier paramêtre est le nom du fichier concerné, possiblement avec
|
|
|
|
|
son chemin d'accès (absolu ou relatif), comme \texttt{"foo.fimg"},
|
|
|
|
|
\texttt{"/var/tmp/foo.data"} ou \texttt{"./bla.txt"}.
|
|
|
|
|
Le second est le mode d'accès à ce fichier. Ce mode précise, entre
|
|
|
|
|
autres, si nous souhaitons lire ou écrire dans ce fichier.
|
|
|
|
|
La valeur retournée par cette fonction est un pointeur qui peut
|
|
|
|
|
être égal à \texttt{NULL} en cas d'erreur~: par exemple le fichier
|
|
|
|
|
n'existe pas ou ne peut pas être crée.
|
|
|
|
|
|
|
|
|
|
Notre fichier est maintenant ouvert, la fonction \texttt{fprintf} va
|
|
|
|
|
nous permettre d'écrire quelque chose dedans. Elle est analogue au
|
|
|
|
|
\texttt{prinf} vu un peu plus tôt, sauf qu'elle demande un
|
|
|
|
|
argument supplémentaire, et c'est justement le pointeur retourné
|
|
|
|
|
par \texttt{fopen}~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
int fprintf(FILE *stream, const char *format, ...);
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Un filtre Unix}\label{filtre-unix}
|
|
|
|
|
|
|
|
|
|
Ce qu'on appelle un "filtre" est un logiciel destiné à transformer
|
|
|
|
|
les données qui le traversent. Pour bien comprendre, le mieux,
|
|
|
|
|
comme d'habitude, est un exemple. Il est un peu artificiel,
|
2023-02-11 22:19:00 +11:00
|
|
|
|
car il s'agit d'éliminer les lettres \textsc{o} et \textsc{p},
|
|
|
|
|
sans raison valable. Le voici~:
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2023-02-11 22:19:00 +11:00
|
|
|
|
\lstinputlisting[language=c]{code/C/no-op.c}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2023-02-11 22:19:00 +11:00
|
|
|
|
Démonstration immédiate~:
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
$ echo apopoz | ./no-op
|
|
|
|
|
az
|
|
|
|
|
$ ./no-op < no-op.c | head -5
|
|
|
|
|
/*
|
|
|
|
|
* n-.c is an useless shell filter
|
|
|
|
|
*/
|
|
|
|
|
#include <stdi.h>
|
|
|
|
|
#include <ctye.h>
|
|
|
|
|
$
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2023-02-11 22:19:00 +11:00
|
|
|
|
Vous voulez un autre exemple ? En voici un, parfois utile pour des
|
|
|
|
|
simulations de terminal de \textsl{h4ck3rZ}\footnote{texte vert ou
|
|
|
|
|
orange sur fond noir de rigueur.} sur une ligne en boucle de
|
|
|
|
|
courant à 300 Bauds, le tout pour un film que l'on souhaite classable
|
|
|
|
|
en série Z.
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[language=c]{code/C/slowprint.c}
|
|
|
|
|
|
|
|
|
|
\vspace{2em}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Pour une explication plus détaillée sur les mécanismes
|
2021-09-27 09:57:38 +11:00
|
|
|
|
utilisés et les usages possibles d'un tel filtre,
|
|
|
|
|
il faut voir le concept de pipeline du shell en
|
2021-08-23 18:54:19 +11:00
|
|
|
|
page \pageref{pipeline}.
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Les pointeurs}\label{pointeur}\index{pointeur}
|
|
|
|
|
|
|
|
|
|
\textbf{Ah, enfin, on a failli attendre !}
|
|
|
|
|
|
|
|
|
|
Qu'est-ce qu'un pointeur ? La réponse est multiple, et c'est
|
|
|
|
|
le pointeur qui fait à la fois la force et la faiblesse du C.
|
|
|
|
|
Pour faire simple, un pointeur est une variable qui contient
|
|
|
|
|
l'adresse dans la mémoire d'une autre variable.
|
|
|
|
|
|
2021-09-29 19:01:34 +11:00
|
|
|
|
Mais en fait c'est un peu plus subtil : un pointeur « connait » la taille
|
|
|
|
|
de l'objet pointé, et peut donc « parcourir » un tableau de ces objets.
|
|
|
|
|
|
2021-09-29 19:03:59 +11:00
|
|
|
|
\begin{verbatim}
|
|
|
|
|
Objet tableau[N];
|
|
|
|
|
Objet *ptr;
|
|
|
|
|
ptr = tableau; // *ptr désigne tableau[0];
|
|
|
|
|
ptr++; // *ptr désigne tableau[1];
|
|
|
|
|
\end{verbatim}
|
2021-09-29 19:01:34 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
XXX il y a beaucoup à dire, et de quoi bien rire \textsl{:)}
|
2021-09-29 19:01:34 +11:00
|
|
|
|
|
2023-06-29 20:23:11 +11:00
|
|
|
|
% https://hackthedeveloper.com/function-pointer-in-c/
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Le préprocesseur}\index{cpp}
|
|
|
|
|
|
|
|
|
|
Nous avons déja abordé de loin la directive \texttt{\#include},
|
|
|
|
|
qui fait partie du préprocesseur, comme toutes les lignes
|
|
|
|
|
de code commençant par le caractère \textbf{\#}.
|
|
|
|
|
Le concept de base, qu'il faut bien capter, est que le
|
|
|
|
|
préprocesseur pratique des \emph{substitutions de texte}.
|
|
|
|
|
Pratiquement, il se passe ça~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
#define NUMID 1664
|
|
|
|
|
#define FLAVOUR "tisane"
|
|
|
|
|
printf("La %d c'est de la %s\n", NUMID, FLAVOUR);
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Ces trois lignes de code
|
|
|
|
|
(deux directives pour cpp et un appel classique de fonction)
|
|
|
|
|
seront converties en une seule ligne de C.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
printf("La %d c'est de la %s\n", 1664, "tisane");
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
C'est donc cette ligne qui sera ensuite passée au vrai compilateur
|
|
|
|
|
pour être traduite en codes opératoires, dont l'exécution
|
|
|
|
|
affichera une maxime dont la véracité demande à être vérifiée
|
|
|
|
|
par l'expérimentation. Mais ça n'est pas fini.
|
|
|
|
|
|
|
|
|
|
Ce sympathique \textsf{préproc'} nous permet aussi de faire
|
|
|
|
|
de la compilation conditinnelle, et ça, c'est cool parce que
|
|
|
|
|
ça ouvre la porte à plein de choses.
|
|
|
|
|
Par exemple, l'instrumentation du code afin de faciliter
|
|
|
|
|
les tests et le debug.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
...
|
|
|
|
|
#ifdef TRACEUR
|
|
|
|
|
fprintf(stderr, "pid %d was here.\n", getpid());
|
|
|
|
|
#endif
|
|
|
|
|
...
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Et à la compilation, il vous faut passer l'option
|
|
|
|
|
\texttt{-DTRACEUR} à Gcc pour que ce message de trace soit
|
|
|
|
|
pris en compte.
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
|
|
|
|
% ---------------------------------------------------------
|
|
|
|
|
|
2021-10-16 08:22:38 +11:00
|
|
|
|
\subsection{Les macros}
|
|
|
|
|
|
|
|
|
|
Une chose très fourbe dont voici un exemple ?
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
2023-04-03 08:32:29 +11:00
|
|
|
|
#define pixidx (fi,x,y) (((y)*fi->width)+(x))
|
|
|
|
|
#define getRpix (fi,x,y) (fi->R[ pixidx(fi,(x),(y)) ])
|
|
|
|
|
#define getGpix (fi,x,y) (fi->G[ pixidx(fi,(x),(y)) ])
|
|
|
|
|
#define getBpix (fi,x,y) (fi->B[ pixidx(fi,(x),(y)) ])
|
2021-10-16 08:22:38 +11:00
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Finalemant, ça n'est pas si compliqué que ça.
|
|
|
|
|
Il suffit juste de savoir protéger les choses fragiles
|
|
|
|
|
avec des parenthèses. Partout.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% =========================================================
|
|
|
|
|
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\section{Unités de compilation}
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Jusque à maintenant, nous n'avons vu que des programmes dont
|
|
|
|
|
le code source n'était que dans un seul fichier, ce qui devient
|
|
|
|
|
vite ingérable pour un gros projet. C permet facilement
|
|
|
|
|
de faire de la compilation séparée~: chacun des fichiers source
|
|
|
|
|
est compilé indépendament en un fichier \textsl{objet}, lesquels
|
|
|
|
|
seront ensuite \textbf{liés} pour obtenir l'exécutable final.
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Mais découper un gros logiciel en plusieurs fichiers source
|
|
|
|
|
a d'autres avantages.
|
2023-04-03 08:32:29 +11:00
|
|
|
|
Bien maitrisé, c'est une technique assez sereine.
|
|
|
|
|
Elle permet d'isoler des données spécifiques qu'il serait
|
|
|
|
|
bien plus lourd et périlleux d'utiliser d'une autre façon.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Les structures}
|
|
|
|
|
|
|
|
|
|
Une structure est une sorte de boite dans laquelle on peut
|
|
|
|
|
ranger plusieurs variables afin de les manipuler comme
|
2023-04-03 08:32:29 +11:00
|
|
|
|
une seule entité. Les utilisateurs de 80 colonnes voient
|
|
|
|
|
tout de suite de quoi il s'agit.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
XXX trouver un exemple parlant et décalé\dots
|
2021-09-28 01:09:50 +11:00
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\section{Gestion de la mémoire}
|
|
|
|
|
\index{malloc} \index{free}
|
2021-07-31 08:45:42 +11:00
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
Nous avons déja entrevu la gestion « implicite » de la mémoire
|
|
|
|
|
avec les variables locales. Il est temps de passer à une gestion
|
|
|
|
|
explicite de celle-ci~:
|
|
|
|
|
les fonctions \texttt{malloc} et \texttt{free} sont là pour ça.
|
|
|
|
|
|
2023-09-04 07:24:56 +11:00
|
|
|
|
La première fonction demande au mc/p de nous préter une certaine quantité
|
2021-08-23 18:54:19 +11:00
|
|
|
|
de mémoire, que nous pourront utiliser à notre guise.
|
2023-09-04 07:24:56 +11:00
|
|
|
|
Et la seconde restitue cette zone mémoire au système sous-jacent.
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
Un rapide synopsis minimal d'utilisation~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
int foo, *ptr;
|
|
|
|
|
if (NULL==(ptr=malloc(sizeof(int)*NBITEMS))) abort();
|
|
|
|
|
for (foo=0; foo<NBITEMS; foo++) ptr[foo] = rand();
|
|
|
|
|
do_something(ptr);
|
|
|
|
|
free(ptr);
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Pour les besoins de la démo, nous avons deux variables, l'une
|
|
|
|
|
est entière (\texttt{foo}) et l'autre, \texttt{ptr}, est
|
|
|
|
|
un pointeur sur $N$ entiers.
|
|
|
|
|
Ce pointeur est initialisé sur la seconde ligne par un appel
|
|
|
|
|
à la fonction \texttt{malloc(3)} avec en paramètre le
|
|
|
|
|
nombre d'\textbf{octets} que nous voulons emprunter.
|
|
|
|
|
Ce nombre est ici calculé en multipliant le nombre de case
|
|
|
|
|
désiré par la taille de la case, une bonne occasion de
|
|
|
|
|
découvrir l'opérateur \texttt{sizeof}\dots
|
|
|
|
|
|
|
|
|
|
La boucle \texttt{for} de la ligne suivante insère des données
|
|
|
|
|
pertinentes dans notre tableau. Lesquelles données seront
|
|
|
|
|
habilement traitées sur la ligne suivante par l'appel
|
|
|
|
|
de cette fonction~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
void do_something(int values[])
|
|
|
|
|
{
|
|
|
|
|
int foo;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for (foo=0; foo<NBITEMS; foo++) sum += (double)values[foo];
|
|
|
|
|
printf("sum is %g\n", sum);
|
|
|
|
|
}
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Et finalement, nous avons réussi à générer un \textsl{useless number}.
|
|
|
|
|
Notre mission est terminée, nous rendons notre bloc de mémoire
|
|
|
|
|
au mc/p avec \texttt{free(ptr);}.
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
\section{Gérer les options}\index{getopt}\label{getopt}
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
int getopt(int argc, char * const argv[], const char *optstring);
|
|
|
|
|
extern char *optarg;
|
|
|
|
|
extern int optind, opterr, optopt;
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
La page de man de getopt(3) contient des explications détaillées
|
|
|
|
|
et un exemple simple d'utilisation.
|
|
|
|
|
|
2023-09-04 07:24:56 +11:00
|
|
|
|
% =========================================================
|
|
|
|
|
% Sun Sep 3 05:48:37 UTC 2023
|
|
|
|
|
%
|
|
|
|
|
%
|
|
|
|
|
\section{Analyser une ligne de texte} \index{parser}
|
|
|
|
|
|
|
|
|
|
Nous désirons implémenter une interface en ligne de commande
|
|
|
|
|
(aka CLI\index{CLI}) pour un enchainement simple d'intructions
|
|
|
|
|
assez semblables. On peut aussi voir ça comme la création
|
|
|
|
|
d'un \textsl{domain specific language} (aka DSL\index{DSL}).
|
|
|
|
|
|
|
|
|
|
Pour être cohérent avec le shell, nous allons utiliser la
|
|
|
|
|
même méthode de séparation des mots sur la ligne à
|
|
|
|
|
décomposer~:
|
|
|
|
|
utiliser une liste pré-définie de séparateur de champs,
|
|
|
|
|
comme le \$IFS du shell.
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
Pour simplifier l'exemple, toutes les commandes susceptibles d'être
|
|
|
|
|
tapées
|
|
|
|
|
seront structurées de la même manière.
|
|
|
|
|
|
2023-09-04 07:24:56 +11:00
|
|
|
|
\subsection{la fonction \texttt{strtok}} \index{strtok}
|
|
|
|
|
|
|
|
|
|
Cette fonction nous permet de « découper » notre ligne de commande
|
|
|
|
|
en \textsl{tokens} par un ensemble de
|
|
|
|
|
caractères de séparation appelé \texttt{delim} dans cet extrait
|
|
|
|
|
du man\index{man}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
#include <string.h>
|
|
|
|
|
char *strtok(char *str, const char *delim);
|
|
|
|
|
|
|
|
|
|
The strtok() function breaks a string into a sequence of zero or more
|
|
|
|
|
nonempty tokens. On the first call to strtok(), the string to be
|
|
|
|
|
parsed should be specified in str. In each subsequent call that should
|
|
|
|
|
parse the same string, str must be NULL.
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Concrètement, nous allons utiliser deux séparateurs très classiques,
|
|
|
|
|
l'espace et la tabulation \texttt{ delim = "\symbol{92}t ";}.
|
|
|
|
|
on peut ensuite aller regarder dans la mémoire ce qui s'y passe~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
--- chaine d'origine
|
|
|
|
|
66 6f 6f 20 20 20 62 61 72 09 71 75 75 78 20 77 69 7a 00
|
|
|
|
|
f o o b a r q u u x w i z
|
|
|
|
|
--- 1er strtok
|
2023-10-09 06:56:41 +11:00
|
|
|
|
66 6f 6f \0 20 20 62 61 72 09 71 75 75 78 20 77 69 7a 00
|
2023-09-04 07:24:56 +11:00
|
|
|
|
f o o b a r q u u x w i z
|
|
|
|
|
got [foo]
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
Donc, \texttt{strtok} a détecté le premier caractère de séparation,
|
|
|
|
|
ici un espace, qui est juste après le premier mot de la ligne à analyser,
|
|
|
|
|
et l'a remplacé par un '\symbol{92}0', le marqueur de fin de chaine.
|
|
|
|
|
Le premier mot (foo) a bien été isolé.
|
2023-09-04 07:24:56 +11:00
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
on peut constater que la chaine de départ est modifiée, ce qui
|
|
|
|
|
risque de causer des soucis, mais on en parlera plus tard.
|
2023-09-04 07:24:56 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\subsection{Le programme complet}
|
|
|
|
|
|
|
|
|
|
\lstinputlisting[language=c]{code/C/demo-strtok.c}
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|
2021-10-20 04:07:28 +11:00
|
|
|
|
|
|
|
|
|
\section{Erreurs classiques}
|
|
|
|
|
|
2023-09-04 07:24:56 +11:00
|
|
|
|
Le C est un langage parsemé de fosses, de chausse-trapes et de
|
|
|
|
|
pièges fourbes et sournois. Sans parler des UBs, hein\dots
|
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\begin{itemize}
|
|
|
|
|
\item{Variables non initialisées.}
|
2021-10-20 04:07:28 +11:00
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\item{Sortir d'un tableau (par le haut ou par le bas).}
|
2021-10-20 04:07:28 +11:00
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\item{Dépassement de la taille d'une chaine.}
|
2021-10-20 04:07:28 +11:00
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\item{Libérer deux fois la mémoire.}
|
2021-10-20 04:07:28 +11:00
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\item{\textsl{file pointer} invalide.}
|
|
|
|
|
\end{itemize}
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
% =========================================================
|
|
|
|
|
|
2022-06-10 06:08:46 +11:00
|
|
|
|
\section{Random} \index{ramdom} \label{c-random}
|
|
|
|
|
|
|
|
|
|
XXX \index{XXX}
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
Un ordinateur est déterministe.
|
|
|
|
|
|
2022-06-10 06:08:46 +11:00
|
|
|
|
% =========================================================
|
|
|
|
|
|
2022-01-30 08:46:41 +11:00
|
|
|
|
\section{Debug} \index{Debug}
|
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
Des outils et astuces en page \pageref{chap:debug}.
|
2022-01-30 08:46:41 +11:00
|
|
|
|
|
|
|
|
|
\begin{quote}
|
|
|
|
|
splint is an annotation-assisted lightweight static checker.
|
|
|
|
|
It is a tool for
|
|
|
|
|
statically checking C programs for security vulnerabilities and coding
|
|
|
|
|
mistakes.
|
|
|
|
|
If additional effort is invested in adding annotations to programs,
|
|
|
|
|
splint can perform stronger checking.
|
|
|
|
|
\end{quote}
|
|
|
|
|
|
|
|
|
|
% =========================================================
|
|
|
|
|
\section{Legalize}
|
|
|
|
|
|
|
|
|
|
"The two forms of conforming implementation are hosted and freestanding.
|
|
|
|
|
A conforming hosted implementation shall accept any strictly conforming
|
|
|
|
|
program. A conforming freestanding implementation shall accept any
|
|
|
|
|
strictly conforming program in which the use of the features specified
|
|
|
|
|
in the library clause (Clause 7) is confined to the contents of the
|
|
|
|
|
standard headers <float.h> , <iso646.h> , <limits.h> , <stdalign.h> ,
|
|
|
|
|
<stdarg.h> , <stdbool.h> , <stddef.h> , <stdint.h> , and <stdnoreturn.h>
|
|
|
|
|
. Additionally, a conforming freestanding implementation shall accept
|
|
|
|
|
any strictly conforming program in which the use of the features
|
|
|
|
|
specified in the header <string.h> , except the following functions:
|
|
|
|
|
strdup , strndup , strcoll , strxfrm , strerror ." (4p6)
|
|
|
|
|
|
2023-01-30 08:16:13 +11:00
|
|
|
|
https://devblogs.microsoft.com/oldnewthing/20230109-00/?p=107685
|
2022-06-10 06:08:46 +11:00
|
|
|
|
|
|
|
|
|
% =========================================================
|
|
|
|
|
|
|
|
|
|
\section{Dessiner}
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
% XXX insert blabla here
|
|
|
|
|
|
2022-06-10 06:08:46 +11:00
|
|
|
|
\subsection{G2} \index{g2} \label{g2}
|
|
|
|
|
|
|
|
|
|
\begin{quote}
|
|
|
|
|
\textbf{g2} is a simple to use graphics library for 2D graphical applications
|
|
|
|
|
written in \textit{ANSI C}. This library provides a comprehensive set of
|
|
|
|
|
functions for simultaneous generation of graphical output on different
|
|
|
|
|
types of devices.
|
|
|
|
|
\end{quote}
|
|
|
|
|
|
2022-09-12 10:21:21 +11:00
|
|
|
|
\lstinputlisting[language=c]{code/g2/un_dessin.c}
|
|
|
|
|
|
2023-10-09 06:56:41 +11:00
|
|
|
|
%
|
|
|
|
|
% XXX EXEMPLE TROP MINIMALISTE !!!
|
|
|
|
|
%
|
2022-09-12 10:21:21 +11:00
|
|
|
|
|
2022-01-30 08:46:41 +11:00
|
|
|
|
% =========================================================
|
|
|
|
|
|
2021-08-23 18:54:19 +11:00
|
|
|
|
\section{Ailleurs dans cet ouvrage}
|
|
|
|
|
|
|
|
|
|
Il y a plein d'autres exemples de code en C, sur des sujets
|
|
|
|
|
divers comme Open Sound Control (page \pageref{chap:OSC}) ou
|
|
|
|
|
libsndfile (page \pageref{chap:son}).
|
|
|
|
|
Et pour les gens du système~:
|
2021-09-17 03:53:40 +11:00
|
|
|
|
l'utilisation des signaux (page \pageref{get-signal}),
|
2021-09-28 01:09:50 +11:00
|
|
|
|
le chargement dynamique d'un \textsl{plug-in} (page \pageref{ex_dlopen}).
|
2021-08-23 18:54:19 +11:00
|
|
|
|
|
|
|
|
|
|
2023-04-03 08:32:29 +11:00
|
|
|
|
% =========================================================
|