C (langage)

Un article de Wikipédia, l'encyclopédie libre.
(Redirigé depuis Langage C)
Aller à : navigation, rechercher
Page d'aide sur l'homonymie Pour les articles homonymes, voir C.
C
Apparu en 1972
Auteur Dennis Ritchie
Développeur Dennis Ritchie, Bell Labs
Paradigme Impérative, procédurale, structurée
Typage Statique, faible
Normes
Influencé par B (CPL, BCPL), ALGOL 60, Lisp
A influencé awk, csh, C++, C#, Objective C, BitC, D, Concurrent C, Java, JavaScript, PHP, Perl
Implémentations GCC, MSVC, Borland C, Watcom C

Le C est un langage de programmation impératif, généraliste, issu de la programmation système. Inventé au début des années 1970 pour réécrire UNIX, C est devenu un des langages les plus utilisés. De nombreux langages plus modernes comme C++, Java et PHP reprennent des aspects de C.

Caractéristiques générales[modifier | modifier le code]

C est un langage de programmation impératif et généraliste. C 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. Il propose un éventail de types entiers et flottants conçus pour pouvoir correspondre directement aux types supportés par le processeur. Il fait en outre un usage intensif de 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 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 utilisées, le langage machine 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 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 « on s'apercevra que les 80 % du code d'un nouveau compilateur sont identiques à ceux des codes des autres compilateurs existant déjà. »[1].

Qualités et défauts[modifier | modifier le code]

C'est un des langages les plus utilisés car :

Ses principaux inconvénients sont :

  • la première version du langage (K&R C) offre particulièrement peu de vérifications lors de la compilation, et il n'offre aucune vérification pendant l'exécution, ce qui fait que des erreurs qui pourraient être automatiquement détectées lors du développement ne le sont que plus tard, souvent au prix d'un plantage du logiciel ;
  • son approche de la modularité est inaboutie ;
  • il ne facilite pas le développement de concepts informatiques plus récents comme la programmation orientée objet ou la gestion d'exceptions qui exigent un effort de rigueur supplémentaire de la part du développeur ;
  • il est difficile d'écrire des programmes portables car le comportement exact des exécutables dépend de l'ordinateur cible ;
  • le support de l'allocation de mémoire et des chaînes de caractères est minimaliste, 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 ;
  • des bugs communs comme le débordement de tampon constituent une faille de sécurité informatique et peuvent être exploités par des logiciels malveillants si le développeur ne fait pas attention ;
  • plusieurs genres d'erreurs ne peuvent être détectées automatiquement qu'à l'aide d'outils supplémentaires et non standardisés, comme lint puis splint.

Histoire[modifier | modifier le code]

Kenneth Thompson (à gauche) et Dennis Ritchie (à droite).

Le 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.

Influences subies[modifier | modifier le code]

Bien que C soit officiellement inspiré de B et de BCPL, on note une forte influence de PL/1 (ou de PL360) ; on a pu dire que C était à Unix et au PDP11 ce que PL1 fut pour la réécriture de Multics.

Par ailleurs, l'expression conditionnelle semble directement inspirée de Lisp ou d'Algol 60.

Normalisations[modifier | modifier le code]

Inventé au début des années 1970, le langage a été complètement décrit pour la première fois en 1978 dans le livre The C Programming Language de Brian Kernighan et Dennis Ritchie. On appelle généralement C traditionnel ou K&R C ce premier langage.

En 1983, l'Institut national américain de normalisation (ANSI) a formé un comité de normalisation 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 (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 1995, le groupe de travail de l'ISO a publié deux correctifs et un amendement à C89. Ces changements assez modestes sont parfois appelés C89 avec amendement 1, ou C94 / C95[2],[3]. 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 ISO 646.

En 1999, une nouvelle évolution du langage est normalisée par l'ISO : C99 (formellement ISO/CEI 9899:1999). Parmi les ajouts, on notera du côté syntaxe 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 ; ou encore le mélange des déclarations avec le reste du code. Au niveau de la sémantique, la gestion des tableaux dynamiques, ou encore des pointeurs restreints a été ajoutée. La bibliothèque standard du C99 s'est vue enrichir de six fichiers d'entêtes depuis la précédente norme.

En 2011, l'ISO ratifie une nouvelle version[4] du standard : C11, formellement ISO/IEC 9899:2011.

Éléments du langage[modifier | modifier le code]

Sources[modifier | modifier le code]

L'usage est de donner l'extension de nom de fichier .c aux fichiers source C. En outre, des fichiers permettant de partager les interfaces, portent le même nom, avec l'extension (suffixe) .h (h pour header en anglais, soit en-tête en français). Chaque fichier .c est compilé séparément. Les rôles des fichiers .c et .h sont répartis ainsi :

  • Les fichiers .c contiennent l'implémentation des fonctions et variables globales.
  • Les fichiers .h contiennent uniquement des déclarations de type, de fonctions, de macros, de variables globales, etc.
  • Lorsqu'un fichier .c ou .h utilise un identificateur déclaré dans un autre fichier .h, alors il inclut ce dernier fichier.
  • Le principe généralement appliqué consiste à écrire un fichier .h pour chaque fichier .c, et à déclarer dans le fichier .h tout ce qui est exporté du fichier .c.

Syntaxe[modifier | modifier le code]

La 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);
	else return 1;
}
{ En Pascal }
function factorielle(n: integer) : integer
begin
	if n > 1 then factorielle := n * factorielle(n - 1)
	else factorielle := 1
end.

Là où Pascal utilise des mots clés comme function, begin, end et then, C utilise des parenthèses et accolades.

Hello world[modifier | modifier le code]

Programme 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 <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ée-sortie de la bibliothèque standard de C.
  • main est le nom de la fonction principale, aussi appelée point d'entrée du programme.
  • int est le type renvoyé par la fonction main. Le type int est implicite en K&R C et en C89, mais plus en C99.
  • Le mot clé void entre les parenthèses signifie que la fonction main ne prend aucun paramètre.
  • Les accolades { et } entourent les instructions constituant le corps de la fonction main.
  • printf est une fonction d'écriture dans la sortie standard (la console par défaut).
  • Le caractère " délimite une chaîne de caractères ; "hello, world\n" dans ce cas.
  • Un point-virgule ; termine toute instruction.
  • L'instruction return 0; indique que la fonction retourne la valeur 0.

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.

Mots clés[modifier | modifier le code]

auto, break, case, char, const (C89), continue, default, do, double, else, enum (C89), extern, float, for, goto, if, inline (C99), int, long, register, restrict (C99), return, short, signed (C89), sizeof, static, struct, switch, typedef, union, unsigned, void (C89), volatile (C89), while, _Bool (C99), _Complex (C99), _Imaginary (C99)[5].

Les termes ci-dessus sont réservés pour être exploités en tant que mots clés, et ne doivent pas être utilisés autrement.

Instructions du préprocesseur[modifier | modifier le code]

#include, #define, #pragma (C89), #if, #ifdef, #ifndef, #elif (C89), #else, #endif, #undef, #line, #error.

Types[modifier | modifier le code]

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. Le type char, généralement utilisé pour représenter un caractère, est un type entier comme les autres, si ce n'est que, selon l'implémentation, il équivaut à signed char ou à unsigned char.

Types entiers, en ordre croissant
Type Taille Magnitude signed
(magnitude minimale exigée par le standard[6])
Magnitude unsigned
(magnitude minimale exigée par le standard[6])
char,
unsigned char,
signed char (C89)
8 bits -127 à 127 0 à 255 (0xFF en hexadécimal noté avec le préfixe 0x de la syntaxe de C)
short (identique à signed short),
unsigned short
16 bits -32 767 à +32 767 0 à 65 535 (0xFFFF)
int (identique à signed int),
unsigned int
≥ 16 bits (taille d'un mot machine) -32 767 à +32 767 0 à 65 535 (0xFFFF)
long (en) (identique à signed long),
unsigned long
32 bits -2 147 483 647 à +2 147 483 647 0 à 4 294 967 295 (0xFFFFFFFF)
long long (identique à signed long long),
unsigned long long (C99)
64 bits -9 223 372 036 854 775 807 à +9 223 372 036 854 775 807 0 à 18 446 744 073 709 551 615 (0xFFFFFFFFFFFFFFF)

Le type enum est un type énuméré.

Il existe des types de nombres à virgule flottante, de précision, donc de longueur en bits, variable ; en ordre croissant :

Types décimaux, en ordre croissant
Type Précision Magnitude
float ≥ 6 chiffres décimaux environ 10-37 à 10+37
double ≥ 10 chiffres décimaux environ 10-37 à 10+37
long double ≥ 10 chiffres décimaux environ 10-37 à 10+37
long double (C89) ≥ 10 chiffres décimaux

C99 a ajouté float complex, double complex et long double complex, représentant les nombres complexes associés.

Types élaborés :

  • struct, union, * pour les pointeurs ;
  • [] pour les tableaux ;
  • () pour les fonctions.

Les versions du langage antérieures à C99 ne proposent pas de type booléen, mais il est possible d'en définir un :

enum boolean {false, true};
typedef enum boolean bool;

ou, en version condensée :

typedef enum boolean {false, true} bool;

C99 ajoute le type _Bool.

Commentaire[modifier | modifier le code]

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. Tout ce qui est compris entre ces symboles est du commentaire, saut de ligne compris :

/* Ceci est un commentaire
   sur deux lignes 
   ou plus */

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

Structures de contrôle[modifier | modifier le code]

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 :

  • les tests (également appelés branchements conditionnels) avec :
    • if (expression) instruction else if (expression) instruction else instruction
    • switch (expression) instruction, avec case et default dans l'instruction
  • les boucles avec :
    • while (expression) instruction
    • for (expression_optionnelle ; expression_optionnelle ; expression_optionnelle) instruction
    • do instruction while (expression)
  • les sauts (branchements inconditionnels) :
    • break
    • continue
    • return expression_optionnelle
    • goto étiquette

Comportements ambigus[modifier | modifier le code]

La 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 bugs de portabilité des codes source écrits en C. Prenons pour exemple[7] 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, C[8] garantit simplement que la valeur absolue du reste est strictement inférieure à la valeur absolue du diviseur[9]. La seule garantie dans cet exemple est donc que le résultat sera compris entre -2 et -1.

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 int correspondant au mot machine peut avoir une taille de 16 bits sur un processeur 16 bits et une taille de 64 bits sur un processeur 64 bits.

Outre les comportements laissés au choix de l'implémentation, des constructions syntaxiquement valables ont un comportement lors de l'exécution complètement indéfini. En plus de la classique division par zéro, on peut signaler l'affectation multiple d'une variable dans la même expression, avec l'exemple[10] :

 i = i++; /* Comportement indéfini. */

Les meilleurs compilateurs décèlent certaines constructions problématiques et peuvent les signaler, mais aucun ne prétend à l'exhaustivité.

Bibliothèques logicielles[modifier | modifier le code]

La bibliothèque standard[modifier | modifier le code]

Article détaillé : Bibliothèque standard du C.

La 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 en-têtes déclarant des types et fonctions de la bibliothèque standard :

  • <assert.h> : pour un diagnostic de conception lors de l'exécution (assert)
  • <ctype.h> : tests et classification des caractères (isalnum, tolower)
  • <errno.h> : gestion minimale des erreurs (déclaration de la variable errno)
  • <math.h> : fonctions mathématiques de base (sqrt, cos) ; nombreux ajouts en C99
  • <signal.h> : gestion des signaux (signal et raise)
  • <stddef.h> : définitions générales (déclaration de la constante NULL)
  • <stdio.h> : pour les entrées/sorties de base (printf, scanf)
  • <stdlib.h> : fonctions générales (malloc, rand)
  • <string.h> : manipulation des chaînes de caractères (strcmp, strlen)
  • <time.h> : manipulation du temps (time, ctime)

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.

Les bibliothèques externes[modifier | modifier le code]

Le langage C étant un des langages les plus utilisés en programmation, de nombreuses bibliothèques ont été créées pour être utilisées avec le C. Fréquemment, lors de l'invention d'un format de données, une bibliothèque ou un logiciel de référence en C existe pour manipuler le format. C'est le cas pour libjpeg, libpng, Expat, les décodeur de référence MPEG, libsocket etc.

Des sources à l'exécutable[modifier | modifier le code]

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 (écrit en Python), ou bien des outils spécifiques à l'environnement de développement intégré (IDE) utilisé.

Les étapes menant des sources au fichier exécutable sont au nombre de quatre : précompilation, compilation, assemblage, édition de liens.

Précompilation[modifier | modifier le code]

Durant cette étape, le préprocesseur effectue plusieurs opérations sur les fichiers sources, en suivant des instructions incluses dans les fichiers eux-mêmes. Le préprocesseur produit alors des fichiers intermédiaires (qui ont généralement l'extension « .i ») pour chaque fichier source, 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.

Compilation[modifier | modifier le code]

Article détaillé : Compilateur.

La 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 :

  • l'analyse lexicale, qui est la reconnaissance des mots clés du langage
  • l'analyse syntaxique, qui analyse la structure du programme et sa conformité avec la norme
  • 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.

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 plate-forme à l'autre, la seconde convertissant le langage intermédiaire en langage d'assemblage dépendant du processeur utilisé sur la plate-forme 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).

Assemblage[modifier | modifier le code]

Cette étape consiste en 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 fichiers objets sont généralement d’extension « .o » sous Unix ou Linux, ou « .obj » avec les outils de développement pour plates-formes 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é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 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.

Édition de liens[modifier | modifier le code]

L'é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 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.

Optimiseurs[modifier | modifier le code]

Des 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.

Environnements de développement[modifier | modifier le code]

Exemples[modifier | modifier le code]

Voici quelques exemples présentant très succinctement quelques propriétés du C. Pour plus d'information, voir le WikiLivre "Programmation C".

Chaînes de caractères[modifier | modifier le code]

Voici l'exemple de fonction de copie de chaîne de caractères donné dans The C Programming Language, 2nd edition, p. 106.

void strcpy(char *s, char *t)
{
    while (*s++ = *t++)
        ;
}

Le principe est de copier les octets-caractères jusqu'à ce que l'on copie le caractère nul, qui marque par convention la fin d'une chaîne en C. La bibliothèque standard de C offre une fonction strcpy similaire, dont le prototype est char *strcpy(char*, const char*).

La boucle while utilise une notation classique mais très brève permise par C, qui a contribué à lui donner une réputation de langage peu lisible. L'expression *s++ = *t++ copie un caractère, retourne sa valeur, et incrémente les pointeurs s et t. Cette boucle n'a pas de corps, car toutes les 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 C[11].

Allocation mémoire[modifier | modifier le code]

La structure int_list représente un élément d'une liste chaînée, contenant des données de type int. Les deux fonctions qui suivent (insert_next et remove_next) servent à ajouter et supprimer un élément de la liste.

/* 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 *new_next);
 
    /* 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é
 * Attention : comportement indéterminé s'il n'y pas d'élément suivant !
 */
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 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.

Références[modifier | modifier le code]

  1. Brian Kernighan et Dennis Ritchie (trad. Thierry Buffenoir), Le langage C [« The C Programming Language »], Paris, Masson,‎ 1983, 1e éd., 218 p. [détail des éditions] (ISBN 2-225-80068-5), p. 4
  2. (en), Samuel P. Harbison III, Guy L. Steel Jr., A Reference Manual, fifth edition, Upper Saddle River, Prentice Hall, 2002, p. 4, (ISBN 0-13-089592-X)
  3. (en), Thomas Wolf, The New ISO Standard for C (C9X), 2000
  4. http://www.iso.org/iso/fr/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=57853
  5. « ISO/IEC 9899:TC3, Section 6.4.1: Keywords », International Organization for Standardization JTC1/SC22/WG14,‎ 2007-09-07
  6. a et b « Sizes of integer types », ISO-IEC 9899, 5.2.4.2.1
  7. donné dans Computer Architecture, a Quantitative Approach, 2e édition, de David A. Patterson et John L. Hennessy, Appendix A-12
  8. La révision en 1999 de la norme spécifie explicitement que le résultat soit -1 [6.5.5 alinéa 6].
  9. (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. 205
  10. (en) comp.lang.c FAQ list · Question 3.3
  11. (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.

Voir aussi[modifier | modifier le code]

Sur les autres projets Wikimedia :

Articles connexes[modifier | modifier le code]

Quelques programmes célèbres écrits en C

Bibliographie[modifier | modifier le code]

Liens externes[modifier | modifier le code]