Table des m atières
GÉNÉRALITÉS SUR LE LANGAGE C ..1
1 - PRÉSENTATION PAR L'EXEMPLE DE QUELQUES INTRUCTIONS DU LANGAGE C 1
1.1 Un exemple de programme en langage C 1
1.2 Structure d'un programme en langage C ..2
1.3 Déclarations .2
1.4 Pour écrire des informations : la fonction printf ..3
1.5 Pour faire une répétition : l'instruction for ..4
1.6 Pour lire des informations : la fonction scanf 4
1.7 Pour faire des choix : l'instruction if .4
1.8 Les directives à destination du préprocesseur ..5
1.9 Un second exemple de programme 6
2 - QUELQUES RÈGLES D'ÉCRITURE ..7
2.1 Les identificateurs .7
2.2 Les mots clés ..7
2.3 Les séparateurs ..8
2.4 Le format libre 8
2.5 Les commentaires .9
3 - CRÉATION D'UN PROGRAMME EN LANGAGE C ..9
3.1 L'édition du programme 10
3.2 La compilation .10
3.3 L'édition de liens .10
3.4 Les fichiers en-tête .10
1 - LA NOTION DE TYPE 13
2 - LES TYPES ENTIERS .14
2.1 Leur représentation en mémoire ..14 2.2 Les différents types d'entiers .14
2.3 Notation des constantes entières ..14
3 - LES TYPES FLOTTANTS .15
3.1 Les différents types et leur représentation en mémoire ..15
3.2 Notation des constantes flottantes 15
4 - LES TYPES CARACTÈRES .16
4.1 La notion de caractère en langage C ..16
4.2 Notation des constantes caractères ..16
5 - INITIALISATION ET CONSTANTES ..17
III. LES OPÉRATEURS ET LES EXPRESSIONS EN LANGAGE C ..19
1 - L'ORIGINALITÉ DES NOTIONS D'OPÉRATEUR ET D'EXPRESSION EN LANGAGE C19
2 - LES OPÉRATEURS ARITHMÉTIQUES EN C 20
2.1 Présentation des opérateurs 20
2.2 Les priorités relatives des opérateurs .20
3 - LES CONVERSIONS IMPLICITES POUVANT INTERVENIR DANS UN CALCUL D'EXPRESSION 21
3.1 Notion d'expression mixte ..21
3.2 Les conversions d'ajustement de type .21
3.3 Les promotions numériques 22
3.4 Le cas du type char .234 - LES OPÉRATEURS RELATIONNELS 24
5 - LES OPÉRATEURS LOGIQUES 25
6 - L'OPÉRATEUR D'AFFECTATION ORDINAIRE 27
6.1 Notion de lvalue ..27
6.2 L'opérateur d'affectation possède une associativité de droite à gauche 28
X Table des matières
6.3 L'affectation peut entraîner une conversion .28
7 - LES OPÉRATEURS D'INCRÉMENTATION ET DE DÉCRÉMENTATION .28
7.1 Leur rôle .28
7.2 Leurs priorités ..29
7.3 Leur intérêt 29
8 - LES OPÉRATEURS D'AFFECTATION ÉLARGIE .30 9 - LES CONVERSIONS FORCÉES PAR UNE AFFECTATION ..31
10 - L'OPÉRATEUR DE CAST ..31
11 - L'OPÉRATEUR CONDITIONNEL ..32
12 - L'OPÉRATEUR SÉQUENTIEL .32
13 - L'OPÉRATEUR SIZEOF ..34
14 - RÉCAPITULATIF DES PRIORITÉS DE TOUS LES OPÉRATEURS .34
EXERCICES 35
1 - LES POSSIBILITÉS DE LA FONCTION PRINTF ..37
1.1 Les principaux codes de conversion 37
1.2 Action sur le gabarit d'affichage ..38 1.3 Actions sur la précision 38 1.4 La syntaxe de printf ..39
1.5 En cas d'erreur de programmation 39
1.6 La macro putchar 40
2 - LES POSSIBILITÉS DE LA FONCTION SCANF 40
2.1 Les principaux codes de conversion de scanf .41
2.2 Premières notions de tampon et de séparateurs .41
2.3 Les premières règles utilisées par scanf 41
2.4 On peut imposer un gabarit maximal .42
2.5 Rôle d'un espace dans le format 42
2.6 Cas où un caractère invalide apparaît dans une donnée .43
2.7 Arrêt prématuré de scanf .43
2.8 La syntaxe de scanf 44
2.9 Problèmes de synchronisation entre l'écran et le clavier 44 2.10 En cas d'erreur ..45
2.11 La macro getchar ..46
EXERCICES 46
1 - L'INSTRUCTION IF ..49
1.1 Blocs d'instructions 49
1.2 Syntaxe de l'instruction if 50
1.3 Exemples 50
1.5 En cas d'imbrication des instructions if .51
2 - L'INSTRUCTION SWITCH 52
2.1 Exemples d'introduction de l'instruction switch .52
2.2 Syntaxe de l'instruction switch .55 3 - L'INSTRUCTION DO WHILE ..56 3.1 Exemple d'introduction de l'instruction do while .56 3.2 Syntaxe de l'instruction do while .57
3.3 Exemples 58
4 - L'INSTRUCTION WHILE ..58
4.1 Exemple d'introduction de l'instruction while .58
4.2 Syntaxe de l'instruction while 59
5- L'INSTRUCTION FOR ..60
5.1 Exemple d'introduction de l'instruction for ..60
5.2 Syntaxe de l'instruction for .61
6 - LES INSTRUCTIONS DE BRANCHEMENT INCONDITIONNEL : BREAK, CONTINUE ET GOTO 62
6.1 L'instruction break .62
6.2 L'instruction continue 63
6.3 L'instruction goto 64
EXERCICES 64
1 - LA FONCTION : LA SEULE SORTE DE MODULE EXISTANT EN ..67
2 - EXEMPLE DE DÉFINITION ET D'UTILISATION D'UNE FONCTION EN C..68
3 - QUELQUES RÈGLES ..70
3.1 Arguments muets et arguments effectifs ..70
3.2 L'instruction return .70
3.3 Cas des fonctions sans valeur de retour ou sans arguments .71
3.4 Les anciennes formes de l'en-tête des fonctions .72
4 - LES FONCTIONS ET LEURS DÉCLARATIONS ..72
4.1 Les différentes façons de déclarer (ou de ne pas déclarer) une fonction .72
4.2 Où placer la déclaration d'une fonction .73
4.3 A quoi sert la déclaration d'une fonction ..73
5 - RETOUR SUR LES FICHIERS EN-TÊTE ..74
6 - EN C, LES ARGUMENTS SONT TRANSMIS PAR VALEUR 74
7 - LES VARIABLES GLOBALES 75
7.1 Exemple d'utilisation de variables globales .76
7.2 La portée des variables globales ..76
7.3 La classe d'allocation des variables globales ..77
8 - LES VARIABLES LOCALES 77
8.1 La portée des variables locales .77
8.2 Les variables locales automatiques .77
8.3 Les variables locales statiques ..78
8.4 Le cas des fonctions récursives .78
9 - LA COMPILATION SÉPARÉE ET SES CONSÉQUENCES .79
9.1 La portée d'une variable globale - la déclaration extern .79
9.2 Les variables globales et l'édition de liens ..80
9.3 Les variables globales cachées - la déclaration static .80
Table des matières XI
10 - LES DIFFÉRENTS TYPES DE VARIABLES, LEUR PORTÉE ET LEUR CLASSE D'ALLOCATION 81
10.1 La portée des variables ..81
10.2 Les classes d'allocation des variables ..81
10.3 Tableau récapitulatif ..82
11 - INITIALISATION DES VARIABLES .82
11.1 Les variables de classe statique .82
11.2 Les variables de classe automatique 83
EXERCICES 83
VII. LES TABLEAUX ET LES POINTEURS .85
1 - LES TABLEAUX À UN INDICE .85
1.1 Exemple d'utilisation d'un tableau en C 85
1.2 Quelques règles ..86
2 - LES TABLEAUX À PLUSIEURS INDICES ..87
2.1 Leur déclaration ..87
2.2 Arrangement en mémoire des tableaux à plusieurs indices ..87
3 - INITIALISATION DES TABLEAUX .88
3.1 Initialisation de tableaux à un indice .88
3.2 Initialisation de tableaux à plusieurs indices ..89
4 - NOTION DE POINTEUR - LES OPÉRATEURS * ET & .89
4.1 Introduction ..89
4.2 Quelques exemples 90
4.3 Incrémentation de pointeurs ..91
5 - COMMENT SIMULER UNE TRANSMISSION PAR ADRESSE AVEC UN POINTEUR 92
6 - UN NOM DE TABLEAU EST UN POINTEUR CONSTANT 93
6.1 Cas des tableaux à un indice .93
6.2 Cas des tableaux à plusieurs indices ..94
7 - LES OPÉRATIONS RÉALISABLES SUR DES POINTEURS ..95
7.1 La comparaison de pointeurs .95
7.2 La soustraction de pointeurs ..95
7.3 Les affectations de pointeurs et le pointeur nul .95 7.4 Les conversions de pointeurs .95
7.5 Les pointeurs génériques .96
8 - LES TABLEAUX TRANSMIS EN ARGUMENT 97 8.1 Cas des tableaux à un indice .97
XII Table des matières
8.2 Cas des tableaux à plusieurs indices ..98
9 - UTILISATION DE POINTEURS SUR DES FONCTIONS 100
9.1 Paramétrage d'appel de fonctions ..100
9.2 Fonctions transmises en argument 101
EXERCICES .101
VIII. LES CHAÎNES DE CARACTÈRES ..103
1 - REPRÉSENTATION DES CHAÎNES .103
1.1 La convention adoptée 103
1.2 Cas des chaînes constantes ..103
1.3 Initialisation de tableaux de caractères ..104
1.4 Initialisation de tableaux de pointeurs sur des chaînes 105 2 - POUR LIRE ET ÉCRIRE DES CHAÎNES 106
3 - POUR FIABILISER LA LECTURE AU CLAVIER : LE COUPLE GETS - SCANF 107
4 - GÉNÉRALITÉS SUR LES FONCTIONS PORTANT SUR DES CHAÎNES .108
4.1 Ces fonctions travaillent toujours sur des adresses 108
4.2 La fonction strlen .108
4.3 Le cas des fonctions de concaténation .109
5 - LES FONCTIONS DE CONCATÉNATION DE CHAÎNES .109
5.1 La fonction strcat .109
5.2 La fonction strncat ..110
6 - LES FONCTIONS DE COMPARAISON DE CHAÎNES 110 7 - LES FONCTIONS DE COPIE DE CHAÎNES .111
8 - LES FONCTIONS DE RECHERCHE DANS UNE CHAÎNE .112
9 - LES FONCTIONS DE CONVERSION ..112
10 - QUELQUES PRÉCAUTIONS À PRENDRE AVEC LES CHAÎNES .113
10.1 Une chaîne possède une vraie fin, mais pas de vrai début ..113
10.2 Les risques de modification des chaînes constantes 114
EXERCICES .114
1 - DÉCLARATION D'UNE STRUCTURE .117
2 - UTILISATION D'UNE STRUCTURE .118
2.1 Utilisation des champs d'une structure 118
2.2 Utilisation globale d'une structure 118
2.3 Initialisations de structures ..119
3 - POUR SIMPLIFIER LA DÉCLARATION DE TYPES : DÉFINIR DES SYNONYMES AVEC TYPEDEF .119
3.1 Exemples d'utilisation de typedef .119
3.2 Application aux structures 120
4 - IMBRICATION DE STRUCTURES 120
4.1 Structure comportant des tableaux 120
4.2 Tableaux de structures ..121
4.3 Structures comportant d'autres structures ..122
5 - À PROPOS DE LA PORTÉE DU MODÈLE DE STRUCTURE .122
6 - TRANSMISSION D'UNE STRUCTURE EN ARGUMENT D'UNE FONCTION123
6.1 Transmission de la valeur d'une structure .123
6.2 Transmission de l'adresse d'une structure : l'opérateur -> ..124
7 - TRANSMISSION D'UNE STRUCTURE EN VALEUR DE RETOUR D'UNE FONCTION 125
EXERCICES .125
1 - CRÉATION SÉQUENTIELLE D'UN FICHIER ..127
2 - LISTE SÉQUENTIELLE D'UN FICHIER .129
3 - L'ACCÈS DIRECT ..130
3.1 Accès direct en lecture sur un fichier existant .130
3.2 Les possibilités de l'accès direct 131
3.3 En cas d'erreur ..132
4 - LES ENTRÉES-SORTIES FORMATÉES ET LES FICHIERS DE TEXTE ..132
5 - LES DIFFÉRENTES POSSIBILITÉS D'OUVERTURE D'UN FICHIER .134
6 - LES FICHIERS PRÉDÉFINIS .134
Table des matières XIII
EXERCICES .135
1 - LES OUTILS DE BASE DE LA GESTION DYNAMIQUE : MALLOC ET FREE138
1.1 La fonction malloc ..138
1.2 La fonction free .139
2 - D'AUTRES OUTILS DE GESTION DYNAMIQUE : CALLOC ET REALLOC140
2.1 La fonction calloc .140
2.2 La fonction realloc ..140
3 - EXEMPLE D'APPLICATION DE LA GESTION DYNAMIQUE : CRÉATION D'UNE LISTE CHAÎNÉE.141
EXERCICE 142
XII. LE PRÉPROCESSEUR ..145
1 - LA DIRECTIVE #INCLUDE ..145
2 - LA DIRECTIVE #DEFINE ..146
2.1 Définition de symboles ..146
2.2 Définition de macros ..147
3 - LA COMPILATION CONDITIONNELLE 149
3.1 Incorporation liée à l'existence de symboles .149
3.2 Incorporation liée à la valeur d'une expression 150
XIII. LES POSSIBILITÉS DU LANGAGE C PROCHES DE LA MACHINE .153
1 - COMPLÉMENTS SUR LES TYPES D'ENTIERS .153
1.1 Rappels concernant la représentation des nombres entiers en binaire ..153
1.2 Prise en compte d'un attribut de signe .154
1.3 Extension des règles de conversions 154
1.4 La notation octale ou hexadécimale des constantes ..155
2 - COMPLÉMENTS SUR LES TYPES DE CARACTÈRES .155
2.1 Prise en compte d'un attribut de signe .155
2.2 Extension des règles de conversion ..156
3 - LES OPÉRATEURS DE MANIPULATION DE BITS .156
3.1 Présentation des opérateurs de manipulation de bits 156
3.2 Les opérateurs bit à bit ..157
3.3 Les opérateurs de décalage ..157
3.4 Exemples d'utilisation des opérateurs de bits .158 4 - LES CHAMPS DE BITS 158
5 - LES UNIONS .159
ANNEXE : LES PRINCIPALES FONCTIONS DE LA BIBLIOTHEQUE STANDARD 163
1 - ENTRÉES-SORTIES (STDIO.H) ..163
1.2 Ecriture formatée .163 1.3 Lecture formatée ..166 1.4 Entrées-sorties de caractères ..168 1.5 Entrées-sorties sans formatage ..170
1.6 Action sur le pointeur de fichier 170
1.7 Gestion des erreurs .171
2 - TESTS DE CARACTÉRES ET CONVERSIONS MAJUSCULES-MINUSCULES (CTYPE.H) 171
4 - FONCTIONS MATHÉMATIQUES (MATH.H) .175
5 - UTILITAIRES (STDLIB.H) .176
CORRECTION DES EXERCICES 179
INDEX 191
Dans ce chapitre, nous vous proposons une première approche d'un programme en langage C, basée sur deux exemples commentés.
Vous y découvrirez (pour l'instant de façon encore "informelle") comment s'expriment les instructions de base (déclaration, affectation, lecture et écriture) ainsi que deux des structures fondamentales (boucle avec compteur, choix).
Nous dégagerons ensuite quelques règles générales concernant l'écriture d'un programme. Enfin, nous vous montrerons comment s'organise le développement d'un programme en vous rappelant ce que sont : l'édition, la compilation, l'édition de liens et l'exécution.
1 - PRÉSENTATION PAR L'EXEMPLE DE QUELQUES INTRUCTIONS DU LANGAGE C
1.1 Un exem ple de program m e en langage C
Voici un exemple de programme en langage C, accompagné d'un exemple d'exécution. Avant d'en lire les explications qui suivent, essayez d'en percevoir plus ou moins le fonctionnement.
#include
#include #define NFOIS 5
main()
{ int i ; float x ; float racx ;
printf ("Bonjour\n") ; printf ("Je vais vous calculer %d racines carrées\n", NFOIS) ;
for (i=0 ; i
printf ("Le nombre %f ne possède pas de racine carrée\n", x) ; else { racx = sqrt (x) ; printf ("Le nombre %f a pour racine carrée : %f\n", x, racx) ; } } printf ("Travail terminé - Au revoir") ;
} ___________________________
Bonjour
Je vais vous calculer 5 racines carrées
Donnez un nombre : 4
Le nombre 4.000000 a pour racine carrée : 2.000000
Donnez un nombre : 2
Le nombre 2.000000 a pour racine carrée : 1.414214
Donnez un nombre : -3
Le nombre -3.000000 ne possède pas de racine carrée
Donnez un nombre : 5.8
Le nombre 5.800000 a pour racine carrée : 2.408319
Donnez un nombre : 12.58 Le nombre 12.580000 a pour racine carrée : 3.546829
Travail terminé - Au revoir
____________________________________________________________________
Nous reviendrons un peu plus loin sur le rôle des trois premières lignes. Pour l'instant, admettez simplement que le symbole NFOIS est équivalent à la valeur 5.
1.2 Structure d'un program m e en langage C
La ligne :
main()
se nomme un "en-tête". Elle précise que ce qui sera décrit à sa suite est en fait le "programme principal" (main). Lorsque nous aborderons l'écriture des fonctions en C, nous verrons que celles-ci possèdent également un tel en-tête ; ainsi, en C, le programme principal apparaîtra en fait comme une fonction dont le nom (main) est imposé.
Le programme (principal) proprement dit vient à la suite de cet en-tête. Il est délimité par les accolades "{" et "}". On dit que les instructions situées entre ces accolades forment un "bloc". Ainsi peut-on dire que la fonction main est constituée d'un en-tête et d'un bloc ; il en ira de même pour toute fonction C. Notez qu'un bloc (comme en Pascal) peut lui-même contenir d'autres blocs (c'est le cas de notre exemple). En revanche, nous verrons qu'une fonction ne peut jamais contenir d'autres fonctions (ce qui était le cas du
Pascal).
1.3 Déclarations
Les trois instructions :
int i ; float x ; float racx ;
sont des "déclarations".
La première précise que la variable nommée n est de type int, c'est-à-dire qu'elle est destinée à contenir des nombres entiers (relatifs). Nous verrons qu'en C il existe plusieurs types d'entiers.
Les deux autres déclarations précisent que les variables x et racx sont de type float, c'est-à-dire qu'elles sont destinées à contenir des nombres flottants (approximation de nombres réels). Là encore, nous verrons qu'en C il existe plusieurs types flottants.
En C, comme en Pascal, les déclarations des types des variables sont obligatoires et doivent être regroupées au début du programme (on devrait plutôt dire : au début de la fonction main). Il en ira de même pour toutes les variables définies dans une fonction ; on les appelle "variables locales" (en toute rigueur, les variables définies dans notre exemple sont des variables locales de la fonction main). Nous verrons également (dans le chapitre consacré aux fonctions) qu'on peut définir des variables en dehors de toute fonction ; on parlera alors de variables globales.
1.4 Pourécrire des inform ations : la fonction printf
L'instruction :
printf ("Bonjour\n") ;
appelle en fait une fonction "prédéfinie" (fournie avec le langage, et donc que vous n'avez pas à écrire vous-même) nommée printf. Ici, cette fonction reçoit un argument qui est :
"Bonjour\n"
Les guillemets servent à délimiter une "chaîne de caractères" (suite de caractères). La notation \n est conventionnelle : elle représente un caractère de fin de ligne, c'est-à-dire un caractère qui, lorsqu'il est envoyé à l'écran, provoque le passage à la ligne suivante. Nous verrons que, de manière générale, le langage C prévoit une notation de ce type (\ suivi d'un caractère) pour un certain nombre de caractères dits "de contrôle", c'est-à-dire ne possédant pas de "graphisme" particulier.
Notez que, apparemment, bien que printf soit une fonction, nous n'utilisons pas sa valeur. Nous aurons l'occasion de revenir sur ce point, propre au langage C. Pour l'instant, admettez que nous pouvons, en C, utiliser une fonction comme ce que d'autres langages nomment une "procédure" ou un "sous-programme".
L'instruction suivante :
printf ("Je vais vous calculer %d racines carrées\n", NFOIS) ;
ressemble à la précédente avec cette différence qu'ici la fonction printf reçoit deux arguments. Pour comprendre son fonctionnement, il faut savoir qu'en fait le premier argument de printf est ce que l'on nomme un "format" ; il s'agit d'une sorte de guide qui précise comment afficher les informations qui sont fournies par les arguments suivants (lorsqu'ils existent). Ici, on demande à printf d'afficher suivant le format :
"Je vais vous calculer %d racines carrées\n"
la valeur de NFOIS, c'est-à-dire, en fait, la valeur 5.
Ce format est, comme précédemment, une chaîne de caractères. Toutefois, vous constatez la présence d'un caractère %. Celui-ci signifie que le caractère qui arrive à sa suite est, non plus du texte à afficher tel quel, mais un "code de format". Ce dernier précise qu'il faut considérer la valeur reçue (en argument suivant, donc ici 5) comme un entier et l'afficher en décimal. Notez bien que tout ce qui, dans le format, n'est pas un code de format, est affiché tel quel ; il en va ainsi du texte " racines carrées\n".
Il peut paraître surprenant d'avoir à spécifier à nouveau dans le code format que NFOIS (5) est entière alors que l'on pourrait penser que le compilateur est bien capable de s'en apercevoir (quoiqu'il ne puisse pas deviner que nous voulons l'écrire en décimal et non pas, par exemple, en hexadécimal). Nous aurons l'occasion de revenir sur ce phénomène dont l'explication réside essentiellement dans le fait que printf est une fonction, autrement dit que les instructions correspondantes seront incorporées, non pas à la compilation, mais lors de l'édition de liens.
Cependant, dès maintenant, sachez qu'il vous faudra toujours veiller à accorder le code de format au type de la valeur correspondante. En l'absence de respect de cette règle, vous risquez fort d'afficher des valeurs totalement fantaisistes.
1.5 Pour faire une répétition : l'instruction for
Comme nous le verrons, en langage C, il existe plusieurs façons de réaliser une "répétition" (on dit aussi une "boucle"). Ici, nous avons utilisé l'instruction for :
for (i=0 ; i
Son rôle est de répéter le bloc (délimité par des accolades "{" et "}") figurant à sa suite, en respectant les consignes suivantes :
- avant de commencer cette répétition, réaliser :
i = 0
- avant chaque nouvelle exécution du bloc (tour de boucle), examiner la condition :
i < NFOIS
si elle est satisfaite, exécuter le bloc indiqué, sinon passer à l'instruction suivant ce bloc.
- à la fin de chaque exécution du bloc, réaliser :
i++
Il s'agit là d'une notation propre au langage C qui est équivalente à :
i = i + 1
En définitive, vous voyez qu'ici notre bloc sera répété cinq fois.
1.6 Pourlire des inform ations : la fonction scanf
La première instruction du bloc répété par l'instruction for affiche simplement le message "Donnez un nombre :". Notez qu'ici nous n'avons pas prévu de changement de ligne à la fin. La seconde instruction du bloc :
scanf ("%f", &x) ;
est un appel de la fonction prédéfinie scanf dont le rôle est de lire une information au clavier. Comme printf, la fonction scanf possède en premier argument un format exprimé sous forme d'une chaîne de caractères, ici :
"%f"
ce qui correspond à une valeur flottante (plus tard, nous verrons précisément sous quelle forme elle peut être fournie ; l'exemple d'exécution du programme vous en donne déjà une bonne idée!). Notez bien qu'ici, contrairement à ce qui se produisait pour printf, nous n'avons aucune raison de trouver, dans ce format, d'autres caractères que ceux qui servent à définir un code de format.
Comme nous pouvons nous y attendre, les arguments (ici, il n'y en a qu'un) précisent dans quelles variables on souhaite placer les valeurs lues. Il est fort probable que vous vous attendiez à trouver simplement x et non pas &x.
En fait, la nature même du langage C fait qu'une telle notation reviendrait à transmettre à la fonction scanf la valeur de la variable x (laquelle, d'ailleurs, n'aurait pas encore reçu de valeur précise). Or, manifestement, la fonction scanf doit être en mesure de ranger la valeur qu'elle aura lue dans l'emplacement correspondant à cette variable, c'est-à-dire à son adresse. Effectivement, nous verrons que & est un "opérateur" signifiant "adresse de".
Notez bien que si, par mégarde, vous écrivez x au lieu de &x, le compilateur ne détectera pas d'erreur. Au moment de l'exécution, scanf prendra l'information reçue en deuxième argument (valeur de x) pour une adresse à laquelle elle rangera la valeur lue. Cela signifie qu'on viendra tout simplement écraser un emplacement indéterminé de la mémoire ; les conséquences pourront alors être quelconques.
1.7 Pour faire des ch oix : l'instruction if
Les lignes :
if (x < 0.0) printf ("Le nombre %f ne possède pas de racine carrée\n", x) ; else { racx = sqrt (x) ; printf ("Le nombre %f a pour racine carrée : %f\n", x, racx) ; }
constituent une instruction de choix basée sur la condition x < 0.0. Si cette condition est vraie, on exécute l'instruction suivante, c'est-à-dire :
printf ("Le nombre %f ne possède pas de racine carrée\n", x) ;
Si elle est fausse, on exécute l'instruction suivant le mot else, c'est-à-dire, ici, le bloc :
{ racx = sqrt (x) ;
printf ("Le nombre %f a pour racine carrée : %f\n", x, racx) ;
}
Notez qu'il existe un mot else mais pas de mot then. La syntaxe de l'instruction if (notamment grâce à la présence de parenthèses qui encadrent la condition) le rend inutile.
La fonction sqrt fournit la valeur de la racine carrée d'une valeur flottante qu'on lui transmet en argument.
Remarques:
1) Une instruction telle que :
racx = sqrt (x) ;
est une instruction classique d'affectation : elle donne à la variable racx la valeur de l'expression située à droite du signe égal. Nous verrons plus tard qu'en C l'affectation peut prendre des formes plus élaborées.
2) Notez qu'il existe en C (comme en Pascal) trois sortes d'instructions :
- des instructions simples, terminées obligatoirement par un point-virgule,
- des instructions de structuration telles que if ou for, - des blocs (délimités par { et }).
Les deux dernières ont une définition "récursive" puisqu'elles peuvent contenir, à leur tour, n'importe laquelle des trois formes.
Lorsque nous parlerons d'instruction, sans précisions supplémentaires, il pourra s'agir de n'importe laquelle des trois formes cidessus.
1.8 Les directives à destination du préprocesseur
Les trois premières lignes de notre programme :
#include
#include
#define NFOIS 5
sont en fait un peu particulières. Il s'agit de "directives" qui seront prises en compte avant la traduction (compilation) du programme. Ces directives, contrairement au reste du programme, doivent être écrites à raison d'une par ligne et elles doivent obligatoirement commencer en début de ligne. Leur emplacement au sein du programme n'est soumis à aucune contrainte (mais une directive ne s'applique qu'à la partie du programme qui lui succède). D'une manière générale, il est préférable de les placer au début, comme nous l'avons fait ici.
Les deux premières directives demandent en fait d'introduire (avant compilation) des instructions (en langage C) situées dans les fichiers stdio.h et math.h. Leur rôle ne sera complètement compréhensible qu'ultérieurement.
Pour l'instant, notez que, dès lors que vous faites appel à une fonction prédéfinie, il est nécessaire d'incorporer de tels fichiers, nommés "fichiers en-têtes", qui contiennent des déclarations appropriées concernant cette fonction : stdio.h pour printf et scanf, math.h pour sqrt. Fréquemment, ces déclarations permettront au compilateur d'effectuer des contrôles sur le nombre et le type des arguments que vous mentionnerez dans l'appel de votre fonction.
Notez qu'un même fichier en-tête contient des déclarations relatives à plusieurs fonctions. En général, il est indispensable d'incorporer stdio.h.
La troisième directive demande simplement de remplacer systématiquement, dans toute la suite du programme, le symbole NFOIS par 5. Autrement dit, le programme qui sera réellement compilé comportera ces instrutions :
printf ("Je vais vous calculer %d racines carrées\n", 5) ; for (i=0 ; i
Notez toutefois que le programme proposé est plus facile à adapter lorsque l'on emploie une directive define.
Remarque importante :
Dans notre exemple, la directive #define servait à définir la valeur d'un symbole. Nous verrons (dans le chapitre consacré au préprocesseur) que cette directive sert également à définir ce que l'on nomme une "macro". Une macro s'utilise comme une fonction ; en particulier, elle peut posséder des arguments. Mais le préprocesseur remplacera chaque appel par la ou les instructions C correspondantes. Dans le cas d'une (vraie) fonction, une telle substitution n'existe pas ; au contraire, c'est l'éditeur de liens qui incorporera (une seule fois quel que soit le nombre d'appels) les instructions machine correspondantes.
1.9 Un second exem ple de program m e
Voici un second exemple de programme destiné à vous montrer l'utilisation du type "caractère". Il demande à l'utilisateur de choisir une opération parmi l'addition ou la multiplication, puis de fournir deux nombres entiers ; il affiche alors le résultat correspondant.
#include main() { char op ; int n1, n2 ; printf ("opération souhaitée (+ ou *) ? ") ; scanf ("%c", &op) ; printf ("donnez 2 nombres entiers : ") ; scanf ("%d %d", &n1, &n2) ; if (op == '+') printf ("leur somme est : %d ", n1+n2) ; else printf ("leur produit est : %d ", n1*n2) ;
}
Ici, nous déclarons que la variable op est de type caractère (char). Une telle variable est destinée à contenir un caractère quelconque (codé, bien sûr, sous forme binaire !).
L'instruction scanf ("%c", &op) permet de lire un caractère au clavier et de le ranger dans op. Notez le code %c correspondant au type char (n'oubliez pas le & devant op). L'instruction if permet d'afficher la somme ou le produit de deux nombres, suivant le caractère contenu dans op. Notez que :
- la relation d'égalité se traduit par le signe == (et non = qui représente l'affectation et qui, ici, comme nous le verrons plus tard, serait admis mais avec une autre signification !).
- la notation '+' représente une constante caractère. En C, contrairement à Pascal, on n'utilise pas les mêmes délimiteurs pour leschaînes (il s'agit de ") que pour les caractères.
Remarquez que, tel qu'il a été écrit, notre programme calcule le produit, dès lors que le caractère fourni par l'utilisateur n'est pas +.
Remarques :
1) On pourrait penser à inverser l'ordre des deux instructions de lecture en écrivant :
scanf ("%d %d", &n1, &n2) ; scanf ("%c", &op) ;
Toutefois, dans ce cas, une petite difficulté apparaîtrait : le caractère lu par le second appel de scanf serait toujours différent de + (ou de *). Il s'agirait en fait du caractère de fin de ligne \n (fourni par la "validation" de la réponse précédente). Le mécanisme exact vous sera expliqué dans le chapitre relatif aux "entrées-sorties conversationnelles" ; pour l'instant, sachez que vous pouvez régler le problème en effectuant une lecture d'un caractère supplémentaire.
2) Au lieu de :
scanf ("%d", &op) ;
on pourrait écrire :
op = getchar () ;
Cette instruction affecterait à la variable op le résultat fourni par la fonction getchar (qui ne reçoit aucun argument — n'omettez toutefois pas les parenthèses !).
D'une manière générale, il existe une fonction symétrique putchar ; ainsi :
putchar (op) ;
affiche le caractère contenu dans op.
Notez que généralement getchar et putchar sont, non pas des "vraies fonctions", mais des macros dont la "définition" figure dans stdio.h.
2 - QUELQUES RÈGLES D'ÉCRITURE
Ce paragraphe vous expose un certain nombre de règles générales intervenant dans l'écriture d'un programme en langage C. Nous y parlerons précisément de ce que l'on appelle les "identificateurs" et les "mots clés", du format libre dans lequel on écrit les instructions, de l'usage des séparateurs et des commentaires.
2.1 Les identificateurs
Les identificateurs servent à désigner les différents "objets" manipulés par le programme : variables, fonctions, etc. Comme dans la plupart des langages, ils sont formés d'une suite de caractères choisis parmi les lettres ou les chiffres, le premier d'entre eux étant nécessairement une lettre.
En ce qui concerne les lettres :
- le caractère "souligné" (_) est considéré comme une lettre. Il peut donc apparaître en début d'un identificateur. Voici quelquesidentificateurs corrects :
lg_lig valeur_5 _total _89
- les majuscules et les minuscules sont autorisées mais ne sont pas équivalentes (contrairement, par exemple, à ce qui se produiten Pascal). Ainsi, en C, les identificateurs ligne et Ligne désignent deux objets différents.
En ce qui concerne la longueur des identificateurs, la norme ANSI prévoit qu'au moins les 31 premiers caractères soient "significatifs" (autrement dit, deux identificateurs qui diffèrent par leurs 31 premières lettres désigneront deux objets différents).
2.2 Les m ots clés
Certains "mots clés" sont réservés par le langage à un usage bien défini et ne peuvent pas être utilisés comme identificateurs. En voici la liste, classée par ordre alphabétique.