\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} \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 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 #include $ \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. % --------------------------------------------------------- \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é. % --------------------------------------------------------- \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 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 (page \pageref{ex_dlopen}). % ---------------------------------------------------------