Introduction
Historique
Le langage C est apparu au début des années 1970. À l'époque, le développement sur les machines informatiques nécessitait l'emploi du langage d'assemblage spécifique à chacune d'elles. Le portage d'un programme, voire d'un système d'exploitation, depuis un modèle de machine vers un autre nécessitait alors une réécriture totale du code. En autorisant l'expression des constructions algorithmiques usuelles (variables, boucles, fonctions...) dans un langage de plus haut niveau (plus facile à lire pour un être humain) qui pouvait être traduit (compilé) dans le langage spécifique à la machine sous-jacente, tout en autorisant l'accès aux ressources de bas niveau de cette machine (les périphériques et la mémoire notamment), le langage C pouvait être considéré comme un langage d'assemblage portable.
Le langage C a été conçu en 1972 par Dennis Richie et Ken Thompson, au laboratoire Bell Labs, en vue de développer un nouveau système d'exploitation, Unix, portable sur différents hardwares. En 1978, Brian Kernigham et Dennis Richie publient une première définition du langage, The Programming Language, connue sous le nom C K&R ou C classique. Vu l'intérêt suscité par le langage, il est adapté par de nombreux développeurs et une standardisation s'avère nécessaire. L'American National Standards Institute (ANSI) débute les travaux en 1983. Ils déboucheront en 1988 sur une nouvelle norme, le standard ANSI C. En 1990, l'ISO adopte telle quelle cette norme (C90). D'autres évolutions du langage ont suivi et ont été normalisées par l'ISO: C99 en 1999, C11 en 2011 et C17 en 2017, dernière version, qui est celle que nous utiliserons pour ce cours.
Avantages
Le langage C reste un langage des plus utilisés, vu ses très nombreux avantages dont les principaux sont :
-
Langage structuré de bas niveau
C est un langage structuré, déclaratif et récursif, offrant des structures classiques de contrôle et de déclaration. Considéré à l'origine comme un langage de haut-niveau (par rapport au langage d'assemblage), il est devenu un langage de bas niveau (par rapport notamment aux langages orientés objets), proche de la machine: chaque instruction du langage est conçue pour être compilée en un nombre d'instructions machine restreint et assez prévisible en termes d'occupation mémoire et de charge de calcul.
-
Portabilité
Les compilateurs et bibliothèques logicielles existent sur la plupart des architectures. Un programme écrit en ANSI C pour un certain système peut donc facilement être déployé sur d'autres systèmes (Windows, Unix, Linux, Pc, mini, mainframe, etc).
-
Code performant
Comme C permet d'utiliser des expressions et opérateurs qui sont très proches du langage machine, il permet le développement de programmes efficients et rapides. Il comporte également peu de code "inutile" (vérifications, etc.) car il est supposé que le programmeur sait ce qu'il écrit. De plus, les types définis par le langage sont conçus pour pouvoir correspondre directement aux types de données supportés par le processeur.
-
Facilité d'apprentissage
C est basé sur un noyau de fonctions et d'opérateurs limité, qui permet la formulation d'expressions simples, mais efficaces. Cette simplicité facilite l'assimilation du langage.
Inconvénients
Tous ces avantages impliquent certains inconvénients auxquels les programmeurs devront faire particulièrement attention:
-
Efficience et compréhensibilité
Le C offre la possibilité d'utiliser des expressions compactes et efficientes. Cependant, leur utilisation nécessite de l'expérience et n'est pas facilement accessible à des débutants. Sans commentaires ou explications, les programmes peuvent par conséquent devenir incompréhensibles, donc inutilisables.
-
Portabilité et bibliothèques de fonctions
La portabilité est l'un des avantages les plus importants de C: en écrivant des programmes qui respectent le standard ANSI-C, nous pouvons les utiliser sur n'importe quelle machine possédant un compilateur ANSI-C. Cependant, si un programmeur désire faire appel à une fonction spécifique de la machine (p.ex: utiliser une carte graphique spéciale), il risque de perdre la portabilité. Ainsi, les avantages d'un programme portable doivent être payés par la restriction des moyens de programmation.
-
Discipline de programmation
C est un langage près de la machine, donc dangereux et bien que C soit un langage de programmation structuré, les compilateurs font peu de vérifications et ne nous forcent pas à adopter un certain style de programmation. Dans un certain sens, tout est permis et la tentation de programmer du 'code spaghetti' est grande. (Même la commande 'goto', si redoutée par les puristes, ne manque pas en C!) Par conséquent, si le programmeur a beaucoup de libertés, il a aussi des responsabilités: il doit veiller à sécuriser son code, adopter un style de programmation strict et programmer de manière structurée et claire.
Premier programme
Voici un premier programme qui affiche un message de bienvenue sur la sortie standard stdout.
#include <stdlib.h>#include <stdio.h>int main () {printf("Hello world\n");exit(0);}
Dans cet exemple,
-
Les lignes 1 et 2 sont des directives du préprocesseur (préfixées par #) qui incluent le contenu des fichiers stdlib.h et stdio.h dans notre source.
-
La ligne 3 est une ligne vide, ignorée par le compilateur.
-
La ligne 4 est le nom de la fonction principale, point d'entrée de l'application.
-
La ligne 5 est l'appel de la fonction printf (déclarée dans
stdio.h) qui affiche sur la sortie standard stdout (écran). -
La ligne 6 est l'appel à la fonction exit (déclarée dans
stdlib.h), instruction qui permet de quitter l'application en renvoyant l'exit code ou status code 0 (fin normale du processus) au shell appelant. -
La ligne 7 indique la fin de la fonction main.
Avant d'exécuter notre application, nous devons compiler la source, c-à-d la traduire en langage machine. La commande pour y arriver est la commande cc (pour C Compiler).
cc pgm1.c
Où pgm1.c est le nom du fichier source. Cette commande, si il n'y a aucune erreur, va générer un programme exécutable, appelé par défaut a.out. Avec l'option -o, nous pouvons spécifier un autre nom pour notre exécutable:
cc -o pgm1 pgm1.c
Le compilateur cc
Le compilateur utilisé durant ce cours est le compilateur cc (C Compiler) d'Ubuntu, qui correspond en réalité à un lien symbolique vers gcc (GNU Compiler Collection). gcc est une suite de logiciels libres de compilation qui gère à la fois le langage C et ses dérivés mais aussi le Java ou encore le Fortran. Afin de limiter cette suite logicielle, nous fixons différentes options de compilation:
-
-std=c17 : ce flag définit le langage standard utilisé pour la compilation; ici ISO C17, la norme la plus récente du langage C, datant de 2017.
-
-pedantic : ce flag indique au compilateur de rapporter tous les avertissements (warnings) liés au respect strict de la norme ISO C utilisée (ici C17).
-
-Wall : ce flag signifie "Warning all" ; il active de nombreux avertissements de compilation.
-
-Wvla : ce flag active un avertissement spécifique si un tableau de taille variable (Variable-Length Array, càd. un tableau dont la taille est déterminée à l'exécution) est défini dans le programme. Alors que le standard ANSI-C (C90) interdisait de tels tableaux, la norme suivante (C99) a intégré leur support et les normes suivantes (C11 & C17) les ont rendus optionnels. Un compilateur respectant le standard C17 peut par conséquent ne pas permettre l'utilisation de VLA (définition de la macro __STDC_NO_VLA__). Le flag -Wvla permet d'éviter les problèmes de portabilité induits par cette ambiguïté de la norme C17 vis-à-vis des VLA.
-
-Wno-unused-variable : ce flag permet d'éviter les avertissements (activés par -Wall) liés aux variables (locales ou statiques) non utilisées durant le processus de développement d'un programme.
-
-Werror : ce flag transforme tous les avertissements en erreurs de compilation.
Pour plus d'informations sur les différentes options du compilateur, n'hésitez pas à consulter le manuel en tapant 'man gcc' dans un terminal.
Le manuel man
Pour rappel, la commande man (manual) permet d’accéder à la documentation Unix-Linux (entrez la commande man man pour plus d'informations). À l'origine, elle servait à accéder aux manuels des commandes d'un shell Unix et à la description des fonctions du langage C. Elle s'est étoffée depuis.
Les pages du manuel sont divisées en plusieurs sections. On retrouve notamment les sections suivantes :
-
man 1 : Commandes utilisateur
-
man 2 : Appels système
-
man 3 : Fonctions de bibliothèque
La section qui nous intéresse dans le cadre de ce cours est bien sûr la 3ième (exéutez la commande man 3 intro pour avoir un descriptif de cette section). Toutes les fonctions de la bibliothèque standard de C (libc) y sont décrites. Par exemple, la commande:
man 3 scanfaffiche la page de manuel de la fonction C scanf. Cette page fournit notamment : la signature de la fonction, la librairie C où elle est définie, une description de ce qu'elle fait et de son mode d'utilisation, le résultat renvoyé et les erreurs qu'elle peut occasionner.
Références
Outre ce syllabus, il vous est proposé de consulter d'autres livres et sites proposant une présentation du langage :
-
C Programming Language, de Brian W. Kernighan et Dennis Ritchie (1978), LE livre de référence sur le langage C
-
The C Beginner's Handbook: Learn C Programming Language basics in just a few hours de Flavio Copes (2020)
-
Bonnes pratiques de codage en langage C de Emmanuel Delahaye (2013)
-
Programmer en langage C: Cours et exercices corrigés de Claude Delannoy (5è Edition, 2009)
Voici des sites proposant des exercices accompagnés de leurs solutions:
Pour terminer, un compilateur en ligne.
Remerciement
Je tiens à remercier Anthony Verriest pour la mise en forme de ce syllabus qui rend sa consultation bien plus agréable.