C (langage) — Wikipédia
C (langage) — Wikipédia
Date de première
1972
version
Paradigme Impératif, procédural, structuré
Auteur Dennis Ritchie, Brian Kernighan
Développeur Dennis Ritchie et Kenneth Thompson, Bell Labs
Typage Statique, faible
Normes ANSI X3.159-1989 (ANSI C, C89)
ISO/CEI 9899:1990 (C90)
ISO/CEI 9899:1990/AMD1:1995 (C95)
ISO/CEI 9899:1999 (C99)
ISO/CEI 9899:2011 (C11)
ISO/CEI 9899:2018 (C18)
ISO/CEI 9899:2024 (C23)
Influencé par BCPL, B, Algol 68, Fortran
A influencé awk, csh, C++, C#, Objective-C, D, Concurrent C, Java, JavaScript, PHP, Perl,
Python
Implémentations GCC, MSVC, Borland C, Clang, TCC
Site web https://www.c-language.org
Extensions de fichiers .c, .h
modifier (https://fr.wikipedia.org/w/index.php?title=C_(langage)&action=edit§ion=0)
Histoire
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 C1.
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 The C
Programming Language 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
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 / C953,4.
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 standard5 : 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 version6 : 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 octobre 2024, l'ISO a publié une nouvelle norme ISO/CEI 9899:2024 ou plus simplement C237.
Caractéristiques générales
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.
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 « on s'apercevra que les 80 % du code d'un
nouveau compilateur sont identiques à ceux des codes des autres compilateurs existant déjà9. »
Qualités et défauts
Cette section ne cite pas suffisamment ses sources (juin 2024). [Développer]
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.
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,
le support très limité de la généricité, malgré l’introduction des expressions à type générique
en C11 ;
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 ;
la faible productivité du langage par rapport aux langages plus récents [réf. souhaitée].
Aperçu de la syntaxe
Hello world
Le programme Hello world est proposé en exemple en 1978 dans The C Programming Language
de Brian Kernighan et Dennis Ritchie. 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. Voici l'exemple
original de la 1re édition de 1978 :
main()
{
printf("hello, world\n");
}
main est le nom de la fonction principale, aussi appelée point d'entrée du programme.
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.
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
return 0;
}
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.
Le mot-clé void entre parenthèses signifie que la fonction n'a aucun paramètre. Il peut être
omis sans ambiguïté lors de la définition d'une fonction. En revanche, s'il est omis lors de la
déclaration de la fonction, cela signifie que la fonction peut recevoir n'importe quels
paramètres10. Cette particularité de la déclaration est considérée comme obsolète dans le
standard en C 201111. On peut noter que dans le standard MISRA C 2004, qui impose des
restrictions au C89 pour des usages nécessitant une plus grande sûreté, le mot-clé void est
L'instruction return 0; indique que la fonction main retourne la valeur 0. Cette valeur est
de type int , et correspond au int devant le main .
Brièveté de la syntaxe
La syntaxe de C a été conçue pour être brève. Historiquement, elle a souvent été opposée à celle
de Pascal13, 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 > 0) return n * factorielle(n - 1);
else return 1;
}
{ En Pascal }
function factorielle(n: integer) : integer
begin
if n > 0 then factorielle := n * factorielle(n - 1)
else factorielle := 1
end.
Langage d'expressions
La brièveté de C ne repose pas que sur la syntaxe. Le grand nombre d'opérateurs disponibles, le
fait que la plupart des instructions contiennent une expression, que les expressions produisent
presque toujours une valeur, et que les instructions de test se contentent de comparer la valeur
de l'expression testée avec zéro, concourent à la brièveté du code source.
Voici l'exemple de fonction de copie de chaîne de caractères — dont le principe est de copier les
caractères jusqu'à avoir copié le caractère nul, qui marque par convention la fin d'une chaîne en
C — donné dans The C Programming Language, 2nd edition, p. 106 :
La boucle while utilise un style d'écriture classique en C, qui a contribué à lui donner une
réputation de langage peu lisible. L'expression *s++ = *t++ contient : deux
opérations sont effectuées dans l'expression de test du while . On considère qu'il faut
maîtriser ce genre de notation pour maîtriser le C14.
Pour comparaison, une version n'utilisant pas les opérateurs raccourcis ni la comparaison
implicite à zéro donnerait :
Sources
Les fichiers sources C sont des fichiers texte, généralement dans le codage des caractères du
système hôte. Ils peuvent être écrits avec un simple éditeur de texte. Il existe de nombreux
éditeurs, voire des environnements de développement intégrés (IDE), qui ont des fonctions
spécifiques pour supporter l'écriture de sources en C.
L'usage est de donner les extensions de nom de fichier .c et .h aux fichiers source C. Les
fichiers .h sont appelés fichiers d'en-tête, de l'anglais header. Ils sont conçus pour être inclus au
début des fichiers source, et contiennent uniquement des déclarations.
fichier .c , et à déclarer dans le fichier .h tout ce qui est exporté par le fichier .c .
La 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 make, SCons, ou bien des outils spécifiques à un
environnement de développement intégré. Les étapes menant des sources au fichier exécutable
sont au nombre de quatre : précompilation, compilation, assemblage, édition de liens. Lorsqu'un
projet est compilé, seuls les fichiers .c font partie de la liste des fichiers à compiler ; les
fichiers .h sont inclus par les directives du préprocesseur contenues dans les fichiers source.
Précompilation
Le préprocesseur C exécute des directives contenues dans les fichiers sources. Il les reconnaît
au fait qu'elles sont en début de ligne, et commencent toutes avec le caractère croisillon # .
Parmi les directives les plus courantes, il y a :
Outre l'exécution des directives, le préprocesseur remplace les commentaires par un espace
blanc, et procède au remplacement des macros. Pour le reste, le code source est transmis tel
quel au compilateur pour la phase suivante. Il faut toutefois que chaque #include dans le
code source soit récursivement remplacé par le code source inclus. Ainsi, le compilateur reçoit
un seul source du préprocesseur, qui constitue l'unité de compilation.
Voici un exemple de fichier source copyarray.h faisant un usage classique des directives du
préprocesseur :
#ifndef COPYARRAY_H
#define COPYARRAY_H
#include <stddef.h>
#endif
Les directives #ifndef , #define et #endif garantissent que le code à l'intérieur n'est
compilé qu'une seule fois même s'il est inclus plusieurs fois. La directive #include
<stddef.h> inclut l'en-tête qui déclare le type size_t utilisé plus bas.
Compilation
l'optimisation de code ;
l'écriture d'un code isomorphe à celui de l'assembleur (et parfois du code assembleur lui-
même quand cela est demandé en option du compilateur).
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.
Assemblage
Cette étape consiste en la génération d'un fichier objet en langage machine pour chaque fichier
de code assembleur. Les fichiers objet sont généralement d’extension .o sur Unix, et .obj
avec les outils de développement pour MS-DOS, Microsoft Windows, VMS, CP/M… Cette phase
est parfois regroupée avec la précédente par établissement d'un flux de données 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.
Pour les compilateurs qui génèrent du code intermédiaire, cette phase d'assemblage peut aussi
être totalement supprimée : c'est une machine virtuelle qui interprétera ou compilera ce langage
en code machine natif. La machine virtuelle peut être un composant du système d'exploitation
ou une bibliothèque partagée.
Édition des liens
L'édition des liens est la dernière étape, et a pour but de réunir tous les éléments d'un
programme. Les différents fichiers objet sont alors réunis, ainsi que les bibliothèques statiques,
pour ne produire qu'un fichier exécutable.
Le but de l'édition de liens 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 des liens échoue si des éléments de code référencés manquent.
Éléments du langage
Éléments lexicaux
Le jeu de caractères ASCII suffit pour écrire en C. Il est même possible, mais inusité, de se
restreindre au jeu de caractères invariants de la norme ISO 646, en utilisant des séquences
d'échappement appelées trigrammes. En général, les sources C sont écrits avec le jeu de
caractères du système hôte. Il est toutefois possible que le jeu de caractères d'exécution ne soit
pas celui du source.
Le C est sensible à la casse. Les caractères blancs (espace, tabulation, fin de ligne) peuvent être
librement utilisés pour la mise en page, car ils sont équivalents à un seul espace dans la plupart
des cas.
Mots clés
Le C89 compte 32 mots-clés, dont cinq qui n'existaient pas en K&R C, et qui sont par ordre
alphabétique :
Ce sont des termes réservés qui ne doivent pas être utilisés autrement.
La dernière révision, C1116, introduit encore sept nouveaux mots-clés avec les mêmes
conventions :
Instructions du préprocesseur
Types
Le 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. Les formes
signées et non signées doivent avoir la même taille.
Contrairement à de nombreux autres langages, le type char est un type entier comme un autre,
bien qu'il soit généralement utilisé pour représenter les caractères. Sa taille est par définition
d'un byte.
Types entiers, en ordre croissant
unsigned char
0 à 255
(C89)
short
−32 767 à 32 767
signed short 16 bits
int
−32 767 à 32 767
signed int 16 bits
long
−2 147 483 647 à 2 147 483 647
signed long 32 bits
unsigned long
0 à 18 446 744 073 709 551 615
long (C99)
Il existe des types de nombre à virgule flottante, de précision, donc de longueur en bits, variable ;
en ordre croissant :
Types élaborés :
Le type _Bool est standardisé par C99. Dans les versions antérieures du langage, il était
courant de définir un synonyme :
Le type void représente le vide, comme une liste de paramètres de fonction vide, ou une
fonction ne retournant rien.
Le type void* est le pointeur générique : tout pointeur de donnée peut être implicitement
converti de et vers void* . C'est par exemple le type retourné par la fonction standard
malloc , qui alloue de la mémoire. Ce type ne se prête pas aux opérations nécessitant de
connaître la taille du type pointé (arithmétique de pointeurs, déréférencement).
Structures
C supporte les types composés avec la notion de structure. Pour définir une structure, il faut
utiliser le mot-clé struct suivi du nom de la structure. Les membres doivent ensuite être
int main()
{
struct Personne p;
p.nom = "Albert";
p.age = 46;
}
Les fonctions peuvent recevoir des pointeurs vers des structures. Ils fonctionnent avec la même
syntaxe que les pointeurs classiques. Néanmoins, l'opérateur -> doit être utilisé sur le pointeur
pour accéder aux champs de la structure. Il est également possible de déréférencer le pointeur
pour ne pas utiliser cet opérateur, et toujours utiliser l'opérateur . .
int main()
{
struct Personne p;
p.nom = "Albert";
p.age = 46;
anniversaire(&p);
}
Commentaire
Dans 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. Presque
tous les langages modernes ont repris cette syntaxe pour écrire des commentaires dans le code.
Tout ce qui est compris entre ces symboles est du commentaire, saut de ligne compris :
La norme C99 a repris de C++ les commentaires de fin de ligne, introduits par deux barres
obliques et se terminant avec la ligne :
Structures de contrôle
La 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 :
continue
return expression_optionnelle
goto étiquette
Fonctions
Les fonctions en C sont des blocs d'instructions, recevant un ou plusieurs arguments et pouvant
retourner une valeur. Si une fonction ne retourne aucune valeur, le mot-clé void est utilisé. Une
fonction peut également ne recevoir aucun argument. Le mot-clé void est conseillé dans ce
cas.
Prototype
Un prototype consiste à déclarer une fonction et ses paramètres sans les instructions qui la
composent. Un prototype se termine par un point-virgule.
// Prototype de saisir
int saisir(void);
// Définition de saisir
int saisir(void)
{
int a;
scanf("%d", &a);
return a;
}
Généralement, tous les prototypes sont écrits dans des fichiers .h, et les fonctions sont définies
dans un fichier .c.
Comportements ambigus
défini par l'implémentation : Le comportement n'est pas spécifié dans la norme mais dépend de
l'implémentation. Le choix effectué dans une implémentation doit être documenté dans celle-
ci. Un programme utilisant ce type de comportement est correct, à défaut d'être garanti
portable.
non spécifié : Le choix n'est pas spécifié dans la norme, mais n'a cette fois pas à être
documenté. Il n'a en particulier pas à être identique à chaque fois pour une même
implémentation. Un programme utilisant ce type de comportement est correct également.
non défini : Comme le nom l'indique, l'opération n'est pas définie. La norme n'impose aucune
limitation à ce que le compilateur peut faire dans ce cas. Tout peut arriver. Le programme est
incorrect.
Un des exemples les plus importants de tel comportement est la taille des types de donnée
entiers. La norme C spécifie la taille minimale des types de base, mais pas leur taille exacte.
Ainsi, le type int par exemple, correspondant au mot machine, doit avoir une taille minimale de
16 bits. Il peut avoir une taille de 16 bits sur un processeur 16 bits et une taille de 64 bits sur un
processeur 64 bits.
Un autre exemple est la représentation des entiers signés20. Il peut s'agir du complément à deux,
du complément à un ou d'un système avec un bit de signe et des bits de valeur (en). La vaste
majorité des systèmes modernes utilise le complément à deux, qui est par exemple le seul
encore supporté par GCC21. De vieux systèmes utilisent les autres formats, comme l'IBM 7090
qui utilise le format signe/valeur, le PDP-1 ou l'UNIVAC et ses descendants, dont certains encore
utilisés actuellement tels le UNIVAC 1100/2200 series#UNISYS 2200 series (en), qui utilisent le
complément à un.
Un autre exemple est le décalage à droite d'un entier signé négatif22. Typiquement,
l'implémentation peut choisir de décaler comme pour un entier non signé ou de propager le bit
de poids fort représentant le signe.
Les comportements non spécifiés23 sont similaires aux comportements définis par
l'implémentation, mais le comportement adopté par l'implémentation n'a pas à être documenté.
Il n'a même pas à être le même en toutes circonstances. Néanmoins, le programme reste
correct, le programmeur ne peut juste pas compter sur une règle particulière.
Par exemple, l'ordre d'évaluation des paramètres lors d'un appel de fonction n'est pas spécifié. Le
compilateur peut même choisir d'évaluer dans un ordre différent les paramètres de deux appels
à la même fonction, si ça peut aider son optimisation.
Comportements indéfinis
Exemples
On peut signaler la classique division par zéro, ou l'affectation multiple d'une variable dans la
même expression avec l'exemple25 :
int i = 4;
i = i++; /* Comportement indéfini. */
On pourrait ainsi penser que dans cet exemple i pourrait valoir 4 ou 5 suivant le choix du
compilateur, mais il pourrait tout aussi bien valoir 42 ou l'affectation pourrait arrêter l'exécution,
ou le compilateur peut refuser la compilation. Aucune garantie n'existe dès qu'un comportement
indéfini existe.
Pour ne citer que quelques exemples, le déréférencement d'un pointeur nul, tout accès à un
tableau hors de ses limites26, l'utilisation d'une variable non initialisée ou encore le débordement
d'entiers signés ont tous des comportements indéfinis. Le compilateur peut utiliser le fait qu'une
construction est indéfinie dans certains cas pour supposer que ce cas ne se produit jamais et
optimiser plus agressivement le code. Si l'exemple ci-dessus peut paraître évident, certains
exemples complexes peuvent être bien plus subtils et être source de bugs parfois graves27,28.
Par exemple, beaucoup de code contient des vérifications destinées à éviter l'exécution dans des
cas hors bornes, qui peut ressembler à ceci29 :
char buffer[BUFLEN];
char *buffer_end = buffer + BUFLEN;
unsigned int len;
/* ... */
En apparence, ce code est prudent et effectue les vérifications de sécurité nécessaires pour ne
pas déborder du buffer alloué. En pratique, les versions récentes de compilateurs tels que GCC,
Clang ou Microsoft Visual C++ peuvent supprimer le second test, et rendre possibles des
débordements. En effet, la norme précise que l'arithmétique de pointeur sur un objet ne peut
donner un pointeur hors de cet objet. Le compilateur peut donc décider que le test est toujours
faux et le supprimer. La vérification correcte est la suivante :
char buffer[BUFLEN];
unsigned int len;
/* ... */
En 2008, quand les développeurs de GCC ont modifié le compilateur pour qu'il optimise certaines
vérifications de débordement qui reposaient sur des comportements indéfinis, le CERT a émis un
avertissement sur l'utilisation des versions récentes de GCC30. Ces optimisations sont en fait
présentes dans la plupart des compilateurs modernes, le CERT a révisé son avertissement dans
ce sens.
Certains outils existent pour détecter ces constructions problématiques, et les meilleurs
compilateurs en décèlent certaines (il faut parfois activer des options particulières) et peuvent
les signaler, mais aucun ne prétend à l'exhaustivité.
Bibliothèques logicielles
La bibliothèque standard
Voici quelques exemples présentant très succinctement quelques propriétés du C. Pour plus
d'information, voir le WikiLivre Programmation C.
Allocation mémoire
La structure int_list représente un élément d'une liste chaînée de nombres entiers. Les
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 *new_next);
return new_next;
}
/*
* Supprimer l'élément suivant un autre.
* node : élément dont le suivant est supprimé
* Attention : comportement indéterminé s'il n'y a pas d'élément
suivant !
*/
void remove_next(struct int_list *node) {
struct int_list *const node_to_remove = node->next;
Dans cet exemple, les deux fonctions essentielles sont malloc et free . La première sert à
allouer de la mémoire, le paramètre qu'elle reçoit est le nombre de bytes que l'on désire allouer et
elle retourne l'adresse du premier byte qui a été alloué, sinon elle retourne NULL. free sert à
libérer la mémoire qui a été allouée par malloc .
UNIX
Noyau Linux
Vim
GNOME
Doom
Notes et références
2. Dennis M. Ritchie et Brian W. Kernighan, Le langage C, Paris, Masson, 1986 [détail des éditions]
(ISBN 2-225-80068-5, lire en ligne (https://openlibrary.org/books/OL21056987M/Le_langage_C) [archive]),
p. 260, 261.
3. (en) Samuel P. Harbison (ill. Guy L. Steele, Jr.), C, a reference manual, Upper Saddle River,
N.J, Prentice-Hall, 2002, 533 p. (ISBN 978-0-13-089592-9 et 978-0-131-22560-2,
OCLC 49820992 (https://worldcat.org/fr/title/49820992)), p. 4.
4. (en), Thomas Wolf, The New ISO Standard for C (C9X), 2000.
5. « ISO/IEC 9899:2011 - Technologies de l'information -- Langages de programmation -- C (htt
p://www.iso.org/iso/fr/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=5785
3) [archive] », sur ISO (consulté le 1er février 2017).
8. Ces particularités se retrouvent dans d'autres langages compilés tels que Fortran et Ada.
11. ISO 9899-2011, section 6.11.6 : « The use of function declarators with empty parentheses
(not prototype-format parameter type declarators) is an obsolescent feature. »
14. (en) Brian Kernighan et Dennis Ritchie, The C Programming Language, Prentice Hall, 1988,
2e éd., 272 p. [détail des éditions] (ISBN 0-13-110362-8), p. 106.
26. Plus précisément, il est autorisé d'accéder à un tableau dans ses limites ou un élément au-
delà, pour faciliter les vérifications de débordement, mais pas plus loin.
27. (en) What Every C Programmer Should Know About Undefined Behavior #1/3 (http://blog.llv
m.org/2011/05/what-every-c-programmer-should-know.html) [archive].
29. exemple issu de (en) Linux Weekly - GCC and pointer overflows (https://lwn.net/Articles/27
8137/) [archive].
30. (en) Vulnerability Note VU#162289 - C compilers may silently discard some wraparound
checks (https://www.kb.cert.org/vuls/id/162289) [archive].
Voir aussi
Bibliographie
(en) Brian Kernighan et Dennis Ritchie, The C Programming Language [détail des éditions]
The international standardization working group for the programming language C (http://www.
open-std.org/JTC1/SC22/WG14/www/standards) [archive],
(en) ISO/CEI 9899:TC2 WG14/N1124, « Committee Draft », 6 mai 2005 [lire en ligne (http://ww
w.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf) [archive]]
(en) ISO/CEI 9899:TC3 WG14/N1256, « Committee Draft », 7 septembre 2007 [lire en ligne (htt
p://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf) [archive]]
(en) ISO/IEC 9899:2017 WG14/N1570, « Committee Draft », 4 avril 2011 [lire en ligne (http://ww
w.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf) [archive]]
ooks.google.fr/books?id=pgclFfFmnWEC&pg=PR2) [archive])
Claude Delannoy, Programmer en langage C : cours et exercices corrigés, Paris, Ed. Eyrolles,
coll. « Collection noire », 2002, 267 p. (ISBN 978-2-212-11072-2,
OCLC 50208529 (https://worldcat.org/fr/title/50208529)), 11e tirage
Articles connexes
Bibliothèque standard du C
Alignement de données