|
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
Le C est en informatique un langage de programmation impératif conçu pour la programmation système. Inventé au début des années 1970 avec Unix, C est devenu un des langages les plus utilisés. De nombreux langages plus modernes comme C++, PHP et Java reprennent des aspects de C.
modifier PrésentationLe langage C peut être qualifié de bas niveau ou peu typé dans le sens où le langage est conçu pour permettre la manipulation directe des mots machine. Il propose un éventail de types entiers et flottants qui correspondent directement aux types supportés par le processeur. Il fait en outre un usage intensif des adresses mémoires avec la notion de pointeur. Il a une notion de type composé, mais ne propose aucune opération qui traite directement des objets de plus haut niveau (fichier informatique, chaîne de caractères, liste…). Ces types plus évolués doivent être traités en manipulant des pointeurs et des types composés. De même, le langage ne propose pas en standard la gestion de la programmation orientée objet, ni de mécanisme d'exception, ou de traitement multitâche. Il existe des fonctions standards pour gérer les entrées-sorties et les chaînes de caractères, mais contrairement à d'autres langages, aucun opérateur spécifique pour améliorer l'ergonomie. Ceci rend aisé le remplacement des fonctions standards par des fonctions spécifiquement conçues pour un programme donné. Le langage C a été utilisé pour rendre le système d'exploitation UNIX plus portable. Il a conservé de cela une très grande efficacité pour tout ce qui concerne le développement système. Ainsi la majorité des grands systèmes d'exploitation entre les années 1975 et 1993 ont été développés en C. Ces caractéristiques en font un langage privilégié quand on cherche à maîtriser les ressources utilisées, le code assembleur généré par les compilateurs étant relativement prévisible et parfois même optimal sur les machines d'architecture RISC à grand nombre de registres. Ce langage est donc extrêmement utilisé dans des domaines comme la programmation embarquée sur microcontrôleurs, les calculs intensifs, l'écriture de systèmes d'exploitation et tous les modules où la rapidité de traitement est importante. Il constitue une bonne alternative aux langages d'assemblage dans ces domaines, avec les avantages d'une syntaxe plus lisible et de la portabilité du code. En contrepartie, la mise au point de programmes en C, surtout s'ils utilisent des structures de données complexes, est plus difficile qu'avec des langages de plus haut niveau. En effet, dans un souci de performance le langage C impose à l'utilisateur de programmer certains traitements (libération de la mémoire, vérification de la validité des index sur les tableaux…) qui sont pris en charge automatiquement dans les langages de haut niveau. Le C étant un langage simple, son compilateur l'est également. Cela se ressent au niveau du temps de développement d'un compilateur C pour une nouvelle architecture de processeur : Kernighan et Ritchie estimaient qu'il pouvait être développé en deux mois1. C'est pour cela qu'il est souvent choisi comme premier langage sur une nouvelle architecture. Beaucoup de limitations du langage C ont été levées dans d'autres langages de programmation qui ont adopté une syntaxe inspirée de celle de C ; outre C++ qui est à l'origine une extension de C enrichi de la notion d'objet, on peut citer Java, JavaScript, PHP et C#. modifier Qualités et défautsC'est un des langages les plus utilisés car :
Ses principaux inconvénients sont :
modifier Évolution du langageLe langage C est apparu au cours de l'année 1972 dans les Laboratoires Bell. Il était développé en même temps que Unix par Dennis Ritchie et Ken Thompson. Ken Thompson avait développé un prédécesseur de C, le langage B, qui est lui-même inspiré de BCPL. Dennis Ritchie a fait évoluer le langage B dans une nouvelle version suffisamment différente pour qu'elle soit appelée C. Par la suite, Brian Kernighan aida à populariser le langage. Il procéda aussi à quelques modifications de dernière minute. En 1978, il fut notamment le principal auteur du livre The C Programming Language décrivant le langage enfin stabilisé ; Ritchie s'était occupé des appendices et des exemples avec Unix. On parle encore de C K&R, K&R C en anglais (pour Kernighan and Ritchie C) lorsqu'on se réfère au langage tel qu'il existait à cette époque. modifier NormalisationsEn 1983, l'Institut national américain de normalisation (ANSI) a formé un comité de normalisation du langage qui a abouti en 1989 à l'approbation de la norme dite ANSI C ou C89 (formellement ANSI X3.159-1989). En 1990, cette norme a également été adoptée par l'Organisation internationale de normalisation (C ISO, formellement ISO/CEI 9899:1990). ANSI C est une évolution du C K&R qui reste extrêmement compatible. Elle reprend quelques idées de C++. En 1999, une nouvelle évolution du langage est normalisée par l'ISO : C99 (formellement ISO/CEI 9899:1999). Parmi les ajouts, on notera les tableaux dynamiques, ainsi que des fonctionnalités (types complexes, mot-clef « restrict », directives agissant sur la simplification des instructions arithmétiques) souhaitables pour les calculs numériques intensifs, domaine habituel de FORTRAN. modifier SourcesEn compilation séparée, les sources sont réparties dans deux types de fichier distincts:
modifier SyntaxeLa syntaxe de C a été conçue pour être brève. Historiquement, elle a souvent été comparée à celle de Pascal, langage impératif également créé dans les années 1970. Voici un exemple avec une fonction factorielle : /* En C (norme ISO) */ int factorielle(int n) { if (n > 1) return n * factorielle(n - 1); return 1; } { En Pascal } function factorielle(n: integer) : integer begin if n > 1 then factorielle := n * factorielle(n - 1) else factorielle := 1 end. On constate que là où Pascal utilise des mots clés comme modifier Hello worldProgramme Hello world proposé en exemple en 1978 dans The C Programming Language de Brian W. Kernighan et Dennis M. Ritchie : #include <stdio.h> main() { printf("hello, world\n"); } Le même programme, conformément à la norme ISO : #include <stdlib.h> #include <stdio.h> int main(void) { printf("hello, world\n"); return EXIT_SUCCESS; }
Créer un programme affichant Hello World est depuis devenu l'exemple de référence pour présenter les bases d'un nouveau langage. modifier Comportements mal définismodifier Comportements laissés au choix de l'implémentationLa norme du langage C laisse la définition exacte du comportement de plusieurs opérations au choix du concepteur du compilateur. Ces comportements sont donc définis par l'implémentation. Cette propriété de C permet au compilateur d'utiliser directement les instructions proposées par le processeur, donc de compiler des programmes exécutables courts et efficaces. En contrepartie, c'est parfois la cause de bogues de portabilité des codes source écrits en C. Prenons pour exemple2 la division entière d'un nombre négatif : -5 / 3. Alors que Fortran, Pascal et Ada spécifient un résultat de -1, et que Modula-3 spécifie un résultat de -2, C3 garantit simplement que la valeur absolue du reste est plus petite que la valeur absolue du diviseur4. Pour le programmeur et l'efficacité de C, le plus important est sans doute le fait que les tailles des types de données de base ne doivent respecter que des garanties minimales. Ainsi, le type modifier Comportements indéfinisEn C, des constructions syntaxiquement valables ont un comportement lors de l'exécution complètement indéfini. Outre la classique division par zéro, on peut signaler l'affectation multiple d'une variable dans la même expression, avec l'exemple5 :
i = i++; /* Résultat indéfini. */
Les meilleurs compilateurs décèlent certaines constructions problématiques et peuvent les signaler, mais aucun ne prétend à l'exhaustivité. modifier Des sources à l'exécutableLa génération d'un exécutable, à partir des fichiers sources se fait en plusieurs étapes, qui sont souvent automatisées à l'aide d'outils comme :
Les étapes menant des sources au fichier exécutable sont au nombre de quatre : précompilation, compilation, assemblage, édition de liens. modifier PréprocesseurDurant cette étape, le préprocesseur effectue plusieurs opérations sur les fichiers sources, dont les instructions (les directives du préprocesseur) sont au sein même de ces fichiers. Le préprocesseur produit alors des fichiers intermédiaires pour chaque fichier source (qui ont généralement l'extension ".c"), qui seront utilisés dans l'étape suivante. Le préprocesseur effectue des remplacements de textes, des inclusions de fichiers (généralement les fichiers d'en-têtes contenant diverses déclarations) avec la possibilité d'effectuer certaines opérations uniquement si certaines conditions sont remplies. C'est également durant cette étape que les commentaires sont supprimés. modifier CompilationLa phase de compilation consiste généralement en la génération du code assembleur (encore lisible par un être humain, mais dépendant du processeur). Pour chaque fichier source, on obtient un fichier en langage d'assemblage. Cette étape est divisée en trois sous-étapes, qui sont :
Par abus de langage, on appelle compilation toute la phase de génération d'un fichier exécutable à partir des fichiers sources. Mais c'est seulement une des étapes menant à la création d'un exécutable. Certains compilateurs C fonctionnent à ce niveau en deux phases, la première générant un fichier compilé dans un langage intermédiaire destiné à une machine virtuelle idéale (voir P-Code) portable d'une plateforme à l'autre, la seconde convertissant le langage intermédiaire en langage d'assemblage dépendant du processeur utilisé sur la plateforme cible. D'autres compilateurs C permettent de ne pas générer de langage d'assemblage, mais seulement le fichier compilé en langage intermédiaire, qui sera interprété ou compilé automatiquement en code natif à l'exécution sur la machine cible (par une machine virtuelle qui sera liée au programme final). modifier AssemblageCette étape consiste à la génération d'un fichier objet pour chaque fichier de code assembleur. Ces fichiers objet sont en langage machine. C'est un format binaire, dépendant du processeur. Les fichier objets sont généralement d’extension ".o" sous Unix ou Linux, ou ".obj" avec les outils de développement pour plateformes Microsoft, Intel, Digital, IBM : DOS, Windows, VMS, CP/M... où les extensions se présentent usuellement sous la forme de trois caractères. Cette phase est parfois regroupée avec la précédente (par établissement d'un flux de donnée interne sans passer par des fichiers en langage intermédiaire ou langage d'assemblage), dans ce cas le compilateur génère directement un fichier objet binaire. Pour les compilateurs qui génèrent du code intermédiaire, cette phase d'assemblage peut aussi être totalement supprimée : c'est la machine virtuelle (liée au programme final, ou utilisée séparément comme chargeur du programme à exécuter sur une machine virtuelle partagée et optimisée spécialement sur la machine hôte) qui interprétera ou compilera ce langage en code machine natif directement sur la machine hôte. Dans ce cas, la machine virtuelle qui interprète le langage intermédiaire ou le compile en code natif optimisé pour la machine hôte, peut être un composant du système d'exploitation ou une bibliothèque partagée installée sur celui-ci, et cette machine virtuelle ne sera même pas incluse dans le programme final livrable. modifier Édition de liensL'édition de liens est la dernière étape et a pour but de réunir tous les éléments d'un programme. Les différents fichiers objets sont alors réunis, ainsi que les bibliothèques statiques, pour ne produire qu'un fichier exécutable. Le but de l'édition de lien est de sélectionner les éléments de code utiles présents dans un ensemble de codes compilés et de bibliothèques, et de résoudre les références mutuelles entre ces différents éléments afin de permettre à ceux-ci de se référencer directement à l'exécution du programme. L'édition de lien peut avoir lieu aussi avec les compilateurs générant du langage intermédiaire, afin de générer un seul fichier facilement installable en langage intermédiaire où toutes les références sont résolues. Mais même cette dernière phase d'édition de liens en langage intermédiaire est parfois aussi supprimée, celle-ci étant alors réalisée uniquement lors du chargement du programme directement par la machine virtuelle hôte. Dans ce cas, elle est le plus souvent remplacée par la création d'un paquetage (regroupant les différents fichiers objets en une seule bibliothèque ou archive, et l'inclusion éventuelle dans cette archive d'un petit fichier de directives permettant de décrire quel fichier de langage intermédiaire doit être utilisé en premier par la machine virtuelle au lancement du programme). On peut constater actuellement un goût prononcé pour les machines virtuelles, en raison de la grande variété des systèmes hôtes et des très rapides évolutions technologiques de celles-ci, car cela facilite le déploiement des programmes, le programmeur n'ayant plus alors besoin de générer autant de programmes exécutables que de types de systèmes cibles. modifier Éléments du langagemodifier Instructions du pré-compilateur
modifier Constantes du pré-compilateurListe non exhaustive de constantes définies par le pré-compilateur :
modifier Mots clés
modifier TypesLe langage C comprend de nombreux types de nombres entiers, occupant plus ou moins de bits. La taille des types n'est que partiellement standardisée : le standard fixe uniquement une taille minimale et une magnitude minimale. Les magnitudes minimales sont compatibles avec d'autres représentations binaires que le complément à deux, bien que cette représentation soit presque toujours utilisée en pratique. Cette souplesse permet au langage d'être efficacement adapté à des processeurs très variés, mais elle complique la portabilité des programmes écrits en C. Chaque type entier a une forme « signée » pouvant représenter des nombres négatifs et positifs, et une forme « non signée » ne pouvant représenter que des nombres naturels. Le type Types entiers, en ordre croissant :
Il existe des types de nombres à virgule flottante, de précision, donc de longueur en bits, variable ; en ordre croissant :
C99 a ajouté Types élaborés :
modifier CommentaireDans les versions de C antérieures à C99, les commentaires devaient commencer par une barre oblique et un astérisque (« /* ») et se terminer par un astérisque et une barre oblique. Tout ce qui est compris entre ces symboles est du commentaire, saut de ligne compris :
/* Ceci est un commentaire
sur deux lignes */
La norme C99 a ajouté la possibilité de faire des commentaires sur une seule ligne, de la même manière qu’en C++ :
// Commentaire comme en C++, valable jusqu'à la fin de la ligne
modifier Structures de contrôleLa syntaxe des différentes structures de contrôle existantes en C est largement reprise dans plusieurs autres langages, comme le C++ bien sûr, mais également Java, C#, PHP ou encore JavaScript. Les trois grands types de structures sont présents :
modifier La bibliothèque standardLa bibliothèque standard normalisée, disponible avec toutes les implémentations, présente la simplicité liée à un langage bas-niveau. Voici une liste de quelques fichiers contenant les déclarations des types et fonctions de la bibliothèque standard :
La bibliothèque standard normalisée n'offre aucun support de l'interface graphique, du réseau, des entrées/sorties sur port série ou parallèle, des systèmes temps réel, des processus ou des threads, ou encore de la gestion avancée des erreurs (comme avec des exceptions structurées). Cela pourrait restreindre d'autant la portabilité pratique des programmes qui ont besoin de faire appel à certaines de ces fonctionnalités, sans l'existence de très nombreuses bibliothèques portables et palliant ce manque ; dans le monde UNIX, ce besoin a aussi fait émerger une autre norme, POSIX.1. modifier OptimiseursDes essais de comparaison entre le C et l'assembleur effectués depuis 1990 sur des machines RISC montrent, de façon tout à fait contre-intuitive, un net avantage au C lorsque toutes les options d'optimisation du compilateur sont activées. Les optimiseurs construisent en effet un graphe chromatique qui leur permet d'allouer sans se tromper les usages de registres de façon quasi-optimale, là où un programmeur serait vite perdu. Ils regroupent de plus directement les instructions en réarrangeant les chargements et sauvegardes de registres pour bénéficier de l'effet pipeline. La même opération effectuée sur un programme source serait envisageable, mais le rendrait quasi impossible ensuite à maintenir. En revanche, les optimiseurs ont moins de latitude concernant la réorganisation d'instructions CISC, plus complexes, et dans ce cas précis les résultats sont moins tranchés. modifier ExemplesVoici quelques exemples présentant très succinctement quelques propriétés du C. Pour plus d'information, voir le WikiLivre "Programmation C" modifier Chaînes de caractèresVoici une implémentation possible de la fonction /* stddef.h déclare le type size_t. */ #include <stddef.h> /* Retourne la longueur de la chaîne pointée par str. */ size_t strlen(const char *str) { /* Pointeur p pour parcourir la chaîne. */ const char *p = str; /* La boucle while suivante utilise une notation classique mais très brève permise par C, qui n'a pas peu contribué à donner à C une réputation de langage peu lisible. La même expression incrémente le pointeur p et compare avec zéro le caractère originellement pointé par p. Cette boucle n'a pas de corps, car toutes les opérations sont effectuées dans l'expression de test. On considère qu'il faut maîtriser ce genre de notation pour maîtriser C (voir Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language, Second Edition, Prentice Hall, ISBN 0-13-110362-8, p. 106. */ while (*p++); /* Ici, le pointeur p pointe sur le caractère suivant le 0 qui marque la fin de la chaîne. | c | h | a | i | n | e | \0 | | ^ ^ str p La longueur est donc p - str - 1. */ return p - str - 1; } modifier Allocation mémoireLa structure /* La gestion de la mémoire n'est pas intégrée au langage mais assurée par des fonctions de la bibliothèque standard. */ #include <stdlib.h> struct int_list { struct int_list *next; /* pointeur sur l'élément suivant */ int value; /* valeur de l'élément */ }; /* * Ajouter un élément à la suite d'un autre. * node : élément après lequel ajouter le nouveau * value : valeur de l'élément à ajouter * Retourne : adresse de l'élément ajouté, ou NULL en cas d'erreur. */ struct int_list *insert_next(struct int_list *node, int value) { /* Allocation de la mémoire pour un nouvel élément. */ struct int_list *const new_next = malloc(sizeof(struct int_list)); /* Si l'allocation a réussi, alors insèrer new_next entre node et node->next. */ if (new_next) { new_next->next = node->next; node->next = new_next; new_next->value = value; } return new_next; } /* * Supprimer l'élément suivant un autre. * node : élément dont le suivant est supprimé */ void remove_next(struct int_list *node) { struct int_list *const node_to_remove = node->next; /* Retire l'élément suivant de la liste. */ node->next = node->next->next; /* Libère la mémoire occupée par l'élément suivant. */ free(node_to_remove); } Dans cet exemple, les deux fonctions essentielles sont modifier Environnements de développementIl existe de nombreux environnements de développement. Certains IDE sont libres tels que :
modifier Références
modifier Voir aussimodifier Articles connexes
modifier Bibliographie
modifier Liens externes
|
|||||||||||||||||||||||||||||||||
| All Right Reserved © 2007, Designed by Stylish Blog. |