You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
453 lines
15 KiB
453 lines
15 KiB
\chapter{Le langage C} |
|
\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. |
|
|
|
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. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Hello World} |
|
|
|
Hop, on y va... |
|
|
|
Le tout début d'un programme en C est l'appel par |
|
le \textsl{runtime} d'une fonction |
|
nommée \texttt{main} qui reçoit deux paramètres |
|
dont l'utilisation est décrite un peu plus bas. |
|
Ces paramètres sont fournis par le système d'exploitation. |
|
|
|
\lstinputlisting[language=c]{code/hello.c} |
|
|
|
Un fois passé l'entrée, nous somme dans la partie active. |
|
Nous appelons à ce moment la fonction \texttt{printf} |
|
qui a pour but d'afficher sur l'écran le texte |
|
passé en paramètre. |
|
|
|
Voila, c'est fait. Nous savons dire bonjour au monde. |
|
Point suivant~: dire boujour à quelqu'un d'autre. Et pour |
|
cela il nous fait récupérer un argument depuis la ligne |
|
de commande. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Arguments} |
|
|
|
Expliquons maintenant les deux paramètres |
|
\texttt{argc} et \texttt{argv} du point d'entrée du programme |
|
(la fonction main). |
|
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 |
|
un tableau de chaines de caractères contenant ces différents mots. |
|
|
|
Ce petit bout de code va nous afficher tout ça~: |
|
|
|
\lstinputlisting[language=c]{code/arguments.c} |
|
|
|
Et voici un exemple d'exécution depuis un shell~:\index{shell} |
|
|
|
\begin{verbatim} |
|
$ ./arguments un deux "trois quatre" |
|
0 ./arguments |
|
1 un |
|
2 deux |
|
3 trois quatre |
|
$ |
|
\end{verbatim} |
|
|
|
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}. |
|
|
|
% --------------------------------------------------------- |
|
\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} : |
|
|
|
% --------------------------------------------------------- |
|
\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. |
|
|
|
XXX\index{XXX} |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Entrées / Sorties} |
|
\index{stdin} \index{stdout} \index{stderr} |
|
|
|
\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. |
|
|
|
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. |
|
|
|
\subsection{Écrire : \texttt{printf}} |
|
\index{printf} |
|
|
|
La fonction \texttt{printf} permet d'afficher le contenu d'une |
|
(ou plusieurs) |
|
variable sous divers formats contrôlables par un petit DSL. |
|
|
|
\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"}, |
|
est appelé \emph{chaine de format}. C'est une chaine de caractères |
|
que l'on peut classer en trois catégories~: |
|
|
|
\begin{itemize} |
|
|
|
\item{\textsl{caractères crus} : ils sont directements poussés vers |
|
la sortie. La plupart d'entre eux donnent le résultat attendu.} |
|
|
|
\item{\textsl{codes de format} : les fragments qui commencent par le |
|
caractère '\%'. Ils servent à contrôler l'apparence de ce qui va |
|
suivre. Par exemple \texttt{\%x} va afficher un int en hexadécimal} |
|
|
|
\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. |
|
} |
|
|
|
\end{itemize} |
|
|
|
Quand à la valeur de retour, elle contient le nombre de caractères |
|
efectivement écrits. Ce nombre peut être différent du nombre attendu, |
|
par exemple si le disque est saturé. |
|
|
|
Ce nombre peut être utilisé pour un affichage de multiples données |
|
en limitant la taille des lignes. |
|
|
|
\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} |
|
|
|
\subsection{Lire : \texttt{scanf}} |
|
\index{scanf} |
|
|
|
Avez-vous bien révisé la section qui cause des pointeurs ? |
|
Êtes-vous prèt à d'étranges comportements ? |
|
Alors les subtilités de scanf vont vous plaire. |
|
|
|
\textit{To be continued...} |
|
|
|
\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} |
|
|
|
|
|
% --------------------------------------------------------- |
|
|
|
\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, |
|
il s'agit d'éliminer les lettres \textsc{o} et \textsc{p}. |
|
Le voici~: |
|
|
|
\lstinputlisting[language=c]{code/no-op.c} |
|
|
|
Démonstration~: |
|
|
|
\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} |
|
|
|
Pour une explication plus détaillée sur les mécanismes |
|
utilisés et les usages possibles d'un tel filtre, |
|
il faut voir le concept de pipeline du shell en |
|
page \pageref{pipeline}. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\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. |
|
|
|
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. |
|
|
|
\begin{verbatim} |
|
Objet tableau[N]; |
|
Objet *ptr; |
|
ptr = tableau; // *ptr désigne tableau[0]; |
|
ptr++; // *ptr désigne tableau[1]; |
|
\end{verbatim} |
|
|
|
|
|
% --------------------------------------------------------- |
|
|
|
\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. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Unités de compilation} |
|
|
|
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. |
|
|
|
Mais découper un gros logiciel en plusieurs fichiers source |
|
a d'autres avantages. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Les structures} |
|
|
|
Une structure est une sorte de boite dans laquelle on peut |
|
ranger plusieurs variables afin de les manipuler comme |
|
une seule entité. |
|
|
|
XXX |
|
|
|
% --------------------------------------------------------- |
|
|
|
\section{Gestion de la mémoire} |
|
\index{malloc} \index{free} |
|
|
|
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. |
|
|
|
La première demande au mc/p de nous préter une certaine quantité |
|
de mémoire, que nous pourront utiliser à notre guise. |
|
Et la seconde restitue la zone mémoire au système sous-jacent. |
|
|
|
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);}. |
|
|
|
% --------------------------------------------------------- |
|
|
|
\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. |
|
|
|
|
|
% ========================================================= |
|
|
|
\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~: |
|
l'utilisation des signaux (page \pageref{get-signal}), |
|
le chargement dynamique d'un \textsl{plug-in} (page \pageref{ex_dlopen}). |
|
|
|
|
|
% ---------------------------------------------------------
|
|
|