Le langage C est un langage de programmation impératif, généraliste et de bas niveau. Inventé au début des années 1970 pour réécrire Unix, le langage C est largement utilisé. De nombreux langages plus modernes comme C++, C#, Java et PHP ou JavaScript ont repris une syntaxe similaire au C et reprennent en partie sa logique. C offre au développeur une marge de contrôle importante sur la machine (notamment sur la gestion de la mémoire) et est de ce fait utilisé pour réaliser les « fondations » (compilateurs, interpréteurs, …) de ces langages plus modernes.

Histoire

vignette|Ken Thompson (à gauche) et Dennis Ritchie (à droite).

Le langage C a été inventé au cours de l'année 1972 dans les Laboratoires Bell. Il était développé en même temps qu'Unix par Dennis Ritchie et Ken Thompson. Kenneth Thompson avait développé le prédécesseur direct de C, le langage B, qui est lui-même largement inspiré de BCPL. Dennis Ritchie a fait évoluer le langage B dans une nouvelle version suffisamment différente, en ajoutant notamment les types, pour qu'elle soit appelée C « », Dennis M. Ritchie..

Bien que C soit directement dérivé de B, Ritchie relève aussi des influences de PL/I, FORTRAN et ALGOL 68. En outre, Ritchie signale que l'équipe était convaincue du bien-fondé de l'idée d'écrire un système d'exploitation dans un langage de plus haut niveau que l'assembleur, un aspect pionnier de Multics, écrit en PL/I.

Par la suite, Brian Kernighan aida à populariser le langage C. Il procéda aussi à quelques modifications de dernière minute. En 1978, Kernighan fut le principal auteur du livre décrivant le langage enfin stabilisé ; Ritchie s'était occupé des appendices et des exemples avec Unix. On appelle aussi ce livre « le K&R », et l'on parle de C traditionnel ou de C K&R lorsqu'on se réfère au langage tel qu'il existait à cette époque.

Normalisation

En 1983, l'Institut national américain de normalisation (ANSI) a formé un comité de normalisation (X3J11) du langage qui a abouti en 1989 à 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 (C90, 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++, notamment la notion de prototype et les qualificateurs de type..

Entre 1994 et 1996, le groupe de travail de l'ISO (ISO/CEI JTC1/SC22/WG14) a publié deux correctifs et un amendement à C90 : ISO/CEI 9899/COR1:1994 Technical Corrigendum 1, ISO/CEI 9899/AMD1:1995 Intégrité de C et ISO/CEI 9899/COR1:1996 Technical Corrigendum 2. Ces changements assez modestes sont parfois appelés C89 avec amendement 1, ou C94 / C95., Thomas Wolf, The New ISO Standard for C (C9X), 2000.. Trois fichiers d'entêtes ont été ajoutés, dont deux concernant les caractères larges et un autre définissant un certain nombre de macros en rapport avec la norme de caractères ISO 646.

En 1999, une nouvelle évolution du langage est normalisée par l'ISO : C99 (formellement ISO/CEI 9899:1999). Les nouveautés portent notamment sur les tableaux de taille variable, les pointeurs restreints, les nombres complexes, les littéraux composés, les déclarations mélangées avec les instructions, les fonctions inline, le support avancé des nombres flottants, et la syntaxe de commentaire de C++. La bibliothèque standard du C a été enrichie de six fichiers d'en-tête depuis la précédente norme.

En 2011, l'ISO ratifie une nouvelle version du standard. : C11, formellement ISO/CEI 9899:2011. Cette évolution introduit notamment le support de la programmation multi-thread, les expressions à type générique, et un meilleur support d'Unicode.

En 2018, l'ISO ratifie une nouvelle version. : formellement ISO/CEI 9899:2018, connue aussi comme C18 ou C17. Cette évolution s'attache à préciser et corriger les points litigieux, et n'introduit aucune nouveauté fonctionnelle.

En , l'ISO a publié une nouvelle norme ISO/CEI 9899:2024 ou plus simplement C23.

Caractéristiques générales

Le C est un langage de programmation procédural et généraliste. Il est qualifié de langage de bas niveau dans le sens où chaque instruction du langage est conçue pour être compilée en un nombre d'instructions machine assez prévisible en termes d'occupation mémoire et de charge de calcul. En outre, il propose un éventail de types entiers et flottants conçus pour pouvoir correspondre directement aux types de donnée utilisés par le processeur. Enfin, il fait un usage intensif des calculs d'adresse mémoire avec la notion de pointeurCes particularités se retrouvent dans d'autres langages compilés tels que Fortran et Ada..

Hormis les types de base, C supporte les types énumérés, composés, et opaques. Il ne propose en revanche aucune opération qui traite directement des objets de plus haut niveau (fichier informatique, chaîne de caractères, liste, table de hachage…). 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 système de gestion d'exceptions. 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é.

Ces caractéristiques en font un langage privilégié quand on cherche à maîtriser les ressources matérielles utilisées, le langage machine et les données binaires générées par les compilateurs étant relativement prévisibles. 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 les modules où la rapidité de traitement est importante. Il constitue une bonne alternative au langage d'assemblage dans ces domaines, avec les avantages d'une syntaxe plus expressive et de la portabilité du code source. Le langage C a été inventé pour écrire le système d'exploitation Unix, et reste utilisé pour la programmation système. Ainsi le noyau de grands systèmes d'exploitation comme Windows et Linux sont développés en grande partie en C.

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 indices sur les tableaux…) qui sont pris en charge automatiquement dans les langages de haut niveau.

Dépouillé des commodités apportées par sa bibliothèque standard, C est un langage simple, et 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 mois car

Qualités et défauts

C'est un des langages les plus utilisés car : il existe depuis longtemps, le début des années 1970 ; il est fondé sur un standard ouvert ; de nombreux informaticiens le connaissent ; il permet la minimisation de l'allocation mémoire nécessaire, son contrôle complet et la maximisation de la performance, notamment par l'utilisation de pointeurs ; il permet la construction de structures de données complexes et ad-hoc, au plus près des besoins ; des compilateurs et bibliothèques logicielles existent sur la plupart des architectures ; il a influencé de nombreux langages plus récents dont C++, Java, C# et PHP ; sa syntaxe en particulier est largement reprise ; il met en œuvre un nombre restreint de concepts, ce qui facilite sa maîtrise et l'écriture de compilateurs simples et rapides ; il ne spécifie pas rigidement le comportement du fichier exécutable produit, ce qui aide à tirer parti des capacités propres à chaque ordinateur ; il permet, par la compilation directe vers le langage machine (via l'assembleur), l'écriture de logiciels qui n'ont besoin d'aucun support à l'exécution (ni bibliothèque logicielle ni machine virtuelle), au comportement prévisible en temps d'exécution comme en consommation de mémoire vive, comme des noyaux de système d'exploitation et des logiciels embarqués.

Ses principaux inconvénients sont : le peu de vérifications offertes par les compilateurs d'origine (K&R C), et l'absence de vérifications à l'exécution, ce qui fait que des erreurs qui auraient pu être automatiquement détectées lors du développement ne l’étaient qu’à l'exécution, donc au prix d’un plantage du logiciel ; sous UNIX, on pouvait utiliser les utilitaires lint et cflow pour éviter de tels mécomptes, des vérifications ont été ajoutées des années 1980 aux années 2010, mais elles restent partielles ; son approche de la modularité, restée au niveau de ce qui se faisait au début des années 1970, et largement dépassée depuis par d'autres langages : il ne facilite pas la programmation orientée objet, il ne permet pas de créer des espaces de noms, la gestion d'exceptions très sommaire ; le support très limité de la généricité, malgré l’introduction des expressions à type générique en C11 ; les subtilités de l'écriture de programmes portables, car le comportement exact des exécutables dépend de l'ordinateur cible ; le support minimaliste de l'allocation de mémoire et des chaînes de caractères, ce qui oblige les programmeurs à s'occuper de détails fastidieux et sources de bugs ; il n'y a notamment pas de ramasse-miettes standard ; les bugs graves qui peuvent être causés par un simple manque d'attention du développeur ; tel le dépassement de tampon qui constitue une faille de sécurité informatique exploitable par les logiciels malveillants ; certaines erreurs ne peuvent être détectées automatiquement qu'à l'aide d'outils supplémentaires et non standardisés, comme lint et Valgrind ; la faible productivité du langage par rapport aux langages plus récents.

Aperçu de la syntaxe

Le programme est proposé en exemple en 1978 dans de Brian Kernighan et Dennis Ritchie. Créer un programme affichant « » est depuis devenu l'exemple de référence pour présenter les bases d'un nouveau langage. Voici l'exemple original de la de 1978 :

main() { printf("hello, world\n"); }

main est le nom de la fonction principale, aussi appelée point d'entrée du programme. Les parenthèses () après main indiquent qu'il s'agit d'une fonction. Les accolades { et } entourent les instructions constituant le corps de la fonction. printf est une fonction d'écriture dans la sortie standard, qui produit l'affichage dans la console par défaut. Les caractères " délimitent une chaîne de caractères ; hello, world\n dans ce cas. Les caractères \n sont une séquence d'échappement représentant le saut de ligne. Un point-virgule ; termine l'instruction expression.

Évolution des pratiques

Le même programme, conforme à la norme ISO et suivant les bonnes pratiques contemporaines : #include <stdio.h>

int main(void) { printf("hello, world\n"); return 0; }

#include <stdio.h> inclut l'en-tête standard <stdio.h> contenant les déclarations des fonctions d'entrées-sorties de la bibliothèque standard du C, dont la fonction printf utilisée ici. int est le type renvoyé par la fonction main. Le type int est le type implicite en K&R C et en C89, et il était couramment omis du temps où l'exemple de Kernighan et Ritchie a été écrit. Il est obligatoire en C99.