TetaTricks/chap/C.tex

831 lines
27 KiB
TeX
Raw Normal View History

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
2024-09-10 23:27:37 +11:00
\lstinputlisting[language=c]{code/C/hello.c}
2021-07-31 08:45:42 +11:00
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
2024-09-10 23:27:37 +11:00
\lstinputlisting[language=c]{code/C/arguments.c}
2021-07-31 08:45:42 +11:00
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}.
2024-09-10 23:27:37 +11:00
. . .
\begin{verbatim}
if (config->optind < config->argc)
for (int i = config->optind; i < config.argc; ++i)
process(config->argv[i]);
\end{verbatim}
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,
2024-07-11 11:23:30 +11:00
et une tout aussi éventuelle 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.
2024-07-11 11:23:30 +11:00
Dans le schéma «~classique~» d'une utilisation interactive,
ces échanges ont lieu par
le truchement d'un terminal, qui peut tout aussi bien
être une \textsl{real glasstty} ou une fenêtre d'un émulateur
graphique de terminal, comme \texttt{xterm}.
2023-04-03 08:32:29 +11:00
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
2024-07-11 11:23:30 +11:00
Nous allons commencer par un exemple simple~: la lecture
de deux nombres entiers depuis l'entrée standard.
Les valeurs lues seront stockée dans deux variables
de type \texttt{int}.
\begin{verbatim}
#include <stdio.h>
...
int foo, bar, nbread;
nbread = scanf("%d %d", &foo, &bar);
\end{verbatim}
Le premier argument est une chaine de caractères contenant
des directives de conversion, semblables à celles
du printf.
Dans cette exemple, nous avons deux fois \texttt{"\%d"}
séparées par une espace. Cette directive veut dire
« lire les chiffres qui arrivent et les convertir
en nombre entier quand il n'y en a plus », une
simple règle de conversion.
Les deux arguments suivants sont les adresses des deux variables
destinées à recevoir les valeurs lues,
ou, en langage commun « faut mettre ÇA LÀ ».
Ce sont donc des pointeurs, on les reconnait au
caractère \& qui précède leur nom.
Quand à la valeur retournée par la fonction scanf, elle
présente un grand intérêt car elle contient le nombre
de lectures élémentaires qui se sont bien passées.
Tous les cas d'erreur ne sont pas couvert, mais c'est
suffisant pour traiter les cas simples de la discipline.
Donc, en toute rigueur, le code que nous venons de voir
devrait s'écrire, en utilisant comme il se doit
le \textsl{yoda coding} pour le test de cette valeur
de retour~:
\begin{verbatim}
...
int foo, bard;
if (2 != scanf("%d %d", &foo, &bar)) {
fputs("FAIL\n", stderr)
abort();
}
...
\end{verbatim}
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
2024-02-23 06:08:21 +11:00
\section{Quelques filtres Unix}\label{filtre-unix}
2021-08-23 18:54:19 +11:00
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
2024-02-23 06:08:21 +11:00
simulations de terminal de \textsl{tipiak}\footnote{Le texte vert ou
orange sur fond noir est de rigueur.} sur une ligne en boucle de
2023-02-11 22:19:00 +11:00
courant à 300 Bauds, le tout pour un film que l'on souhaite classable
en série Z.
\lstinputlisting[language=c]{code/C/slowprint.c}
2024-02-23 06:08:21 +11:00
% \vspace{2em}
Vous prendrez bien soin de contempler le traitement d'un éventuel
paramètre passé par la ligne de commande,
et surtout le calcul alambiqué\footnote{On doit quand même
pouvoir faire plus clair\dots}
pour convertir une vitesse en Bauds à un délay acceptable
par \texttt{nanosleep(2)}.
2023-02-11 22:19:00 +11:00
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 !}
2024-07-11 11:23:30 +11:00
Qu'est-ce qu'un pointeur ?
La réponse est multiple et parfois obscure, et c'est
2021-08-23 18:54:19 +11:00
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.
2024-07-11 11:23:30 +11:00
Mais en fait c'est un peu plus subtil :
un pointeur « connait » le type (donc la taille)
2021-09-29 19:01:34 +11:00
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
2024-07-11 11:23:30 +11:00
\index{XXX}
XXX il y a encore 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/
2024-07-11 11:23:30 +11:00
\subsection{Pointeurs et chaines}
2024-08-08 22:56:21 +11:00
\begin{verbatim}
char *text = "this is a text";
char text[] = "this is a text";
\end{verbatim}
Vous le voyez, le piège ?
2024-07-11 11:23:30 +11:00
\subsection{Pointeurs et fonctions}
Exemple classique : qsort.
2023-04-03 08:32:29 +11:00
% =========================================================
2021-08-23 18:54:19 +11:00
\section{Le préprocesseur}\index{cpp}
2024-07-11 11:23:30 +11:00
\textbf{Attention !} L'utilisation abusive du préprocesseur
peut générer des trolls de plusieurs centaines de posts.
2021-08-23 18:54:19 +11:00
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.
2024-07-11 11:23:30 +11:00
Ce sympathique \textsf{préproc'}%
\footnote{Certains l'appellent même tendrement « mon cépépé »
avec un regard de guimauve mal grillée.}
nous permet aussi de faire
2021-08-23 18:54:19 +11:00
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
2024-09-29 05:25:02 +11:00
(aka CLI\index{cli}) pour un enchainement simple d'intructions
2023-09-04 07:24:56 +11:00
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
2024-02-23 06:08:21 +11:00
% =========================================================
%
% Message-ID: <ur80s3$677$1@dont-email.me>
%
\section{Advanced}
Direct from Usenet (\texttt{comp.lang.c}):
\textsl{I did an experiment where is possible to create "sets" of warnings.}
\begin{verbatim}
#define SAFE_REGION \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic error \"-Wenum-compare\"")\
_Pragma("GCC diagnostic error \"-Wparentheses\"")\
_Pragma("GCC diagnostic error \"-Wuninitialized\"")
#define RESTORE \
_Pragma("GCC diagnostic pop")
enum E1 { A };
enum E2 { B };
SAFE_REGION
int main() {
int a, b;
if (a = b){}
if (A == B){}
}
RESTORE
\end{verbatim}
\begin{quote}
J'avoue avoir mis un certain temps à capter. Je vais attendre
le \textsl{follow-up} de Bart pour avoir confirmation.
\end{quote}
% =========================================================
\section{La suite?}
% Message-ID: <657f1dd4$0$10079$426a74cc@news.free.fr>
% NNTP-Posting-Date: 17 Dec 2023 17:12:04 CET
Étudier les différents modêles de mémoire partagée.
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}),
2024-02-23 06:08:21 +11:00
le chargement dynamique d'un \textsl{plug-in} (page \pageref{ex_dlopen}),
les \texttt{ioctl}, \dots
2021-08-23 18:54:19 +11:00
2023-04-03 08:32:29 +11:00
% =========================================================