121
Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 0 ~ A. P. ~ ~ A. P. ~ IUT A Lyon 1 IUT A Lyon 1 Informatique Informatique U C L B Année 2003 ~2004 Année 2003 ~2004 Programmation procédurale du C-ANSI au C++ Programmation procédurale Programmation procédurale du C du C - - ANSI au C++ ANSI au C++ ~ A. P. ~ ~ A. P. ~ IUT A Lyon 1 IUT A Lyon 1 Informatique Informatique U C L B Année 2003 ~2004 Année 2003 ~2004 Programmation procédurale du C-ANSI au C++ Programmation procédurale Programmation procédurale du C du C - - ANSI au C++ ANSI au C++

du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Embed Size (px)

Citation preview

Page 1: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 0

~ A. P. ~~ A. P. ~

IUT A Lyon 1IUT A Lyon 1

InformatiqueInformatique

UUCCLLBBAnnée 2003 ~2004Année 2003 ~2004

Programmation procéduraledu C-ANSI au C++

Programmation procéduraleProgrammation procéduraledu Cdu C--ANSI au C++ANSI au C++

~ A. P. ~~ A. P. ~

IUT A Lyon 1IUT A Lyon 1

InformatiqueInformatique

UUCCLLBBAnnée 2003 ~2004Année 2003 ~2004

Programmation procéduraledu C-ANSI au C++

Programmation procéduraleProgrammation procéduraledu Cdu C--ANSI au C++ANSI au C++

Page 2: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 1

Programmation procédurale - du C-ANSI au C++

Chapitre 1 Eléments du langage C-ANSI page 1 Chapitre 2 Les types construits page 23 Chapitre 3 Les fonctions page 43 Chapitre 4 Les fichiers page 51 Chapitre 5 Compléments page 63 Chapitre 6 Les programmes multi-fichiers page 69 Chapitre 7 Du C-ANSI au C++ en programmation procédurale page 83 Annexe A La bibliothèque standard page 93 Annexe B Squelette d'un programme en langage C page 115 Annexe C Table ASCII page 117 Annexe D Bibliographie succincte page 119 Avertissement: Le présent document constitue le support papier du cours de programmation du premier semestre de première année. L'auteur n'autorise pas son utilisation ou sa diffusion en dehors du Département Informatique de l'IUT-A de l'Université Lyon-I. Merci de signaler les erreurs ou fautes constatées. Bonne lecture. A. P.

Page 3: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 2

CHAPITRE 1 - ELEMENTS DE BASE DU LANGAGE C-ANSI 1 Introduction: Ce polycopié s’adresse aux étudiants en première année du département Informatique de l’IUT-A de l’Université LYON-I. Il a pour but de présenter les éléments du langage C dans l’environnement système Unix, mais n’a pas pour vocation d’être un cours sur les concepts et méthodes de programmation. Il existe à ce propos, d’autres documents au département auxquels il faut se référer. Il en est de même pour le développement de programme C ou C++ avec l'outil Visual Studio. Le lecteur trouvera en annexe une bibliographie indicative, sachant que le manuel qui fait autorité reste l’ouvrage de KERNIGHAN et RITCHIE intitulé " C-ANSI " publié aux éditions MASSON. En particulier sont évoqués dans l’ouvrage cité, l’historique et les évolutions du langage C. 2 L’organisation d’un programme C : Le langage C est dédié à la programmation procédurale. Un programme est un ensemble de séquences d’opérations sur des structures de données qui se déroulent suivant un schéma établi par un algorithme. La philosophie de la programmation procédurale est de décomposer et de structurer un programme pour faciliter sa construction et sa maintenance. Les niveaux de décomposition en C sont dans l’ordre hiérarchique décroissant: • le module : c’est un fichier texte comprenant la définition de fonctions • la fonction : c’est un ensemble de blocs • le bloc : c'est un ensemble d'instructions; un bloc est délimité par les caractères { et } • l'instruction: une instruction est une structure de contrôle ou une expression; une

expression se termine par la caractère; La décomposition dépend de la complexité du problème à traiter. Ainsi suivant le cas, un programme peut être obtenu à partir : • d’un seul module qui ne comprend qu’une seule fonction (cas rare) • d’un seul module comprenant plusieurs fonctions • de plusieurs modules comprenant eux-mêmes une ou plusieurs fonctions. Parmi l’ensemble des fonctions qui constituent un programme, il en existe une particulière qui constitue le point d’entrée du programme et qui s’appelle main(). Il ne doit y avoir qu’une seule fonction main() par programme, même s'il est composé de plusieurs modules.

Page 4: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 3

3 Production d’un exécutable : Un programme exécutable est donc le résultat de la compilation et de l’édition des liens entre modules. La compilation donne à partir d’un fichier texte appelé source un module objet. L’édition des liens est la phase qui permet à partir de plusieurs modules objets créés par l’utilisateur ou déjà existants dans une bibliothèque, d’obtenir un exécutable. On obtient dans l'environnement Unix donc le schéma de production suivant : Fichier texte: le nom du fichier comprend l’extension .c Compilation Phase de traduction par le compilateur Fichier objet : le nom est identique au fichier source mais l’extension est maintenant .o Edition des liens Fichier exécutable d’extension .e

Autre fichier objet

Fichier objet

Bibliothèque de fichiers objects

Fichier source

Fichier exécutable

Page 5: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 4

Afin de faciliter la phase de compilation pour les premiers TP qui ne nécessitent pas la réalisation de plusieurs modules, il existe au département, une procédure de compilation appelée ccomp. Pour invoquer cette procédure il suffit de passer la commande : ccomp tp1.c Cette procédure génère soit un fichier exécutable d’extension .e (tp1.e dans l’exemple), soit un fichier des erreurs d’extension .err (tp1.err ici). La compilation séparée de plusieurs modules en vue de l’obtention d’un exécutable est abordée au chapitre 6 du présent document. 4 Structure d’un fichier texte C : Nous ne nous intéressons dans un premier temps qu’aux programmes C ne comprenant qu’un seul module texte; les principes de la programmation modulaire sont décrits au chapitre 6. Dans cette optique, le niveau de décomposition d'un programme est alors la fonction avec une fonction particulière appelée main(). Une fonction est paramètrable de sorte que le traitement qu’elle effectue soit généralisable. La fonction main() n’échappe pas à cette règle, mais dans un premier temps, on considérera qu’il s’agit d’une fonction particulière qui ne reçoit aucun argument et qui ne retourne rien. Un programme constitué à partir d’un seul module comprend donc :

Des définitions généralesLa fonction main

fonction 1 fonction 2

Exemple : Soit le programme qui souhaite la bienvenue aux nouveaux étudiants :

#include <stdio.h> char pnom[25] ;

void main(void) {

printf("\n Entrez votre prenom : " ) ; scanf("%s",pnom) ; printf("\n\n Bienvenue a l’IUT %s",pnom) ;

}

Page 6: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 5

Ce programme amène plusieurs remarques : 1. même un programme très simple invoque des fonctions qui ont déjà été écrites et qui sont

archivées en tant que modules objet dans une bibliothèque. Ici les fonctions d’entrée-sortie scanf et printf sont des fonctions de la bibliothèque standard fournie avec le compilateur C.

2.pour utiliser des fonctions de la bibliothèque standard, il faut annoncer le type de paramètres qu’elles attendent et le type de valeur retournée s’il y en a. Cette annonce est réalisée par un prototype de la fonction qui indique ces renseignements. C’est le rôle de la directive #include d’inclure les fichiers où se trouvent les prototypes des fonctions de la bibliothèque qui vont être utilisées.

3.la directive #include n’est pas destinée au compilateur, mais au préprocesseur. Le préprocesseur effectue une analyse du texte source avant le compilateur et effectue l’inclusion du texte indiqué chaque fois qu’il rencontre cette directive. Ici on inclut un texte appelé <stdio.h>. Ce fichier est appelé un fichier d’en-tête (ou headers d’ou le .h). Tous les headers se trouvent sous le répertoire /usr/include/ ou dans /usr/local/include. Il s’agit donc de fichiers texte contenant des définitions et des prototypes de fonctions.

4. pour indiquer qu’une fonction ne reçoit pas d’argument, on met (void) après son identificateur. Pour indiquer qu’elle ne retourne rien on met void devant son identificateur.

5 Eléments du langage C : 5-1 Identificateurs, symboles, mots réservés : Un identificateur est un nom donné à une variable, une constante ou une fonction. Il est défini par le programmeur, et peut être constitué à partir de lettres (minuscules et majuscules étant différentes), du caractère de soulignement _ et de chiffres. Cependant un identificateur ne doit pas commencer par un chiffre. Les implémentations récentes du C-ANSI admettent des identificateurs pouvant aller jusqu'à 31 caractères. Les symboles sont des caractères ou groupe de caractères utilisés principalement pour représenter des opérateurs ou des caractère spéciaux. & ~ " # { } ( ) [ ] | \ / * - + % , ; . < > = ! ?

Page 7: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 6

Les mots réservés sont des identificateurs servant de mots-clés au langage C. auto break case char const continue default do double else enum extern float for goto if int long register return short sizeof static struct switch typedef union unsigned void volatile while Les commentaires dans un programme C commencent par la séquence /* et se terminent par la séquence */ quel que soit le nombre de lignes entre les deux. On ne peut pas inclure un commentaire dans un commentaire. 5-2 Les objets manipulés : Un objet au sens de la programmation procédurale est caractérisé par : 1) un identificateur 2) un type qui impose :

• une représentation interne • des opérateurs associés

3) une valeur variable ou constante

a) Les types de base : type char : codé sur un octet et considéré comme un entier sur 8 bits signés ou non

(unsigned); un caractère est représenté par le code ASCII donné en annexe type int : entier en représentation interne en complément à 2; ils sont signés ou non

signés(unsigned) . On distingue : short :codés sur 2 octets

long :codés sur 4 octets int :codés sur 2 ou 4 octets suivant la machine cible

type float : nombre réel en représentation interne suivant la norme IEEE 754. On distingue :

float : simple précision codé sur 4 octets double : double précision codés sur 8 octets

Page 8: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 7

b) Mécanisme de construction de type : En dehors des types de base, il existe également le type tableau, le type structure, le type union, et le type pointeur. Ces types sont étudiés au chapitre 2. Enfin le langage C offre la possibilité au programmeur de construire lui-même ses propres types grâce à la directive typedef. Il s'agit en fait de renommer un type existant (soit un type de base soit un type construit) afin d'en améliorer la compréhension suivant le contexte. La syntaxe pour définir un type nouveau est la suivante: typedef type Nouveautype;

Exemple:

typedef int Age; /* Age est maintenant un type nouveau */ Age anciennete; /* anciennete est une variable de type Age */ c) Les constantes :

constantes entières : on les exprime généralement en décimal ou en hexadécimal précédées

alors de 0x.Une constante correspondant à un entier long est suffixée par la lettre L

Exemple : 127 constante en base 10 0x7F constante en base 16 127L constante entier long en base 10 0177 constante entier long en base 8 constantes réelles : on les exprime soit avec une partie fractionnaire, soit sous forme mantisse

plus exposant Exemple : 25.8 forme fractionnaire 1.03e-2 forme avec exposant constantes chaînes : en C, il n’existe pas de type primitif chaîne. Une chaîne est traitée

comme une suite d’octets se terminant par le caractère '\0' (octet au 0 binaire). On peut définir une constante chaîne de caractères en donnant sa valeur encadrée par les caractères spéciaux " "; il s’agira d’une suite d’octets terminée par '\0'. On peut aussi utiliser le mécanisme de substitution de texte offert par le préprocesseur, mais il ne s’agira alors que de définir des constantes symboliques qui ne pourront pas être utilisées en tant que chaîne constante.

Page 9: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 8

Exemple : "ceci est un message" chaîne constante terminée par '\0' #define TAILLE 10 substitution dans le texte de TAILLE par 10 constantes caractère : une constante caractère est de type entier mais codée sur un octet. Le

codage peut se faire de plusieurs façons: • on écrit le caractère entre apostrophes • on code le caractère en donnant la valeur décimal de con code ASCII • on code le caractère sous la forme '\ooo' ou la séquence ooo est la

représentation en base 8 du code ASCII du caractère à coder • on code le caractère sous la forme '\xhhh' où la séquence hhh est

la représentation en base 16 du code ASCII du caractère à coder Exemple : c='A' c est le caractère A c=65 idem mais on donne le code ASCII de A en décimal c='\101' code ASCII de A est en octal c='\x41' code ASCII de A est en hexadécimal Il existe également des caractères spéciaux permettant de coder des caractères courants. Ces caractères sont notés sous la forme '\lettre'. On donne ici les caractères spéciaux les plus usuels. '\b' caractère backspace (retour arrière) '\n' caractère saut de ligne (new line) '\t' caractère de tabulation horizontale '\\' caractère \

d) Les variables : On définit une variable en précisant son type et son identificateur. On ne peut pas utiliser une variable non définie. Il est possible en langage C d’initialiser une variable au moment de sa définition. Exemple:

int x=3; /* definition de la variable x comme entier initialisé à la valeur 3 */

Page 10: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 9

Les termes de déclaration et de définition sont souvent confondus. Ils ne sont pourtant pas porteur du même sens. Une définition implique une allocation mémoire, celle ci étant faite au moment de la traduction du source, alors qu'une déclaration ne constitue que l'association d'un identificateur à un type. Il n'y a pas alors de réservation mémoire; nous verrons ainsi qu'un objet peut être déclaré plusieurs fois dans le cas de la programmation modulaire, alors qu'il ne doit être défini qu'une seule fois. La définition d'un objet implique également des règles de visibilité (c'est à dire où cet objet peut être atteint). Les règles de portée des variables sont étudiées au chapitre 3. Le C-ANSI permet de préciser au moment de la définition d'un objet que ce dernier ne devra pas être modifié. On utilise pour cela le qualificatif const. Etant donné que l'objet ne pourra plus être modifié par la suite, il doit donc être initialisé au moment de sa définition. Exemple:

const int x=3; 5-3 Les opérateurs : Les opérateurs permettent de construire des expressions. Ils spécifient les calculs à effectuer sur leurs arguments appelés opérandes qui sont soit des objets (variables ou constantes) soit des sous-expressions.

a) Les expressions : Une expression est construite à partir d'opérandes ou d'expressions, reliés par un opérateur. Un opérateur ne peut s'appliquer que sur un certain type d'opérande. En conséquence une expression a elle même un type résultant suivant les types des opérandes et de l'opérateur qui la composent et en fonction de certaines conversions étudiées plus loin. Exemples: • l'expression x + y est constitué de l'opérateur + et des opérandes x et y qui peuvent être

de type int ou float. Le résultat est de type int si x et y sont de type int; il est de type float si x et/ou y sont de type float.

• l'expression x + (y*z) est constituée de l'opérande x et de la sous-expression (y*z).

Le type du résultat est soit int soit float suivant le type des opérandes.

Page 11: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 10

b) Les opérateurs arithmétiques : Il y a quatre opérateurs arithmétiques de base applicables sur des opérandes de type entier ou réel; le type char étant considéré comme un entier on pourra donc lui appliquer également les opérateurs arithmétiques. Outre ces opérateurs de base, on dispose pour les entiers uniquement, de l'opérateur modulo qui représente le reste de la division entière. + addition - soustraction * multiplication / division % modulo (sur les entiers uniquement) Le résultat (sauf pour le modulo) est fonction du type des opérandes. Si l'un des deux opérandes est de type réel, le résultat de l'expression devient réel. Au moment de l'affectation de l'expression, des règles de conversion sont mises en œuvre; ces règles sont étudiées plus loin. Exemple: Supposons la variable définie de la sorte: int i=5; Dans ces conditions l'expression i/3 donne 1; en effet le résultat de l'expression est entier car les 2 opérandes sont entiers. En revanche l'expression i/3.0 donne 1.666667; le résultat de l'expression est réel car un des 2 opérandes est réel.

c) Les opérateurs d'incrémentation : Pour les types entiers et réels, le C propose des opérateurs d'incrémentation et de décrémentation. Ces opérateurs sont notés: ++ incrémentation -- décrémentation Suivant l'écriture, il s'agit de pré-incrémentation ou de post-incrémentation ; de même pour la décrémentation. Exemples: considérons les définitions suivantes (on suit dans l'ordre l'évolution des variables): int i=1; int j, k, l, m; j=++i;

incrémente i de 1 (pré-incrémentation) puis affecte i à j; donc i vaut 2 et j vaut 2

Page 12: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 11

k=i++; affecte i à k puis incrémente i de 1 (post-incrémentation); donc k vaut 2 et i vaut 3 l=--i;

décrémente i de 1 (pré-décrémentation) puis affecte i à l; donc i vaut 2 et l vaut 2

m=i--; affecte i à m puis décrémente i de 1 (post-décrémentation); donc m vaut 2 et i vaut 1 i++;

incrémente i de 1

j--; décrémente j de 1

d) Les opérateurs de traitement de bits : Ces opérateurs s'appliquent uniquement à des opérandes de type entier, et sont utiles pour intervenir directement sur le contenu en mémoire d'une variable. Il s'agit des opérateurs suivants: ~ opérateur unaire de complémentation << opérateur de décalage à gauche >> opérateur de décalage à droite & opérateur ET bit à bit | opérateur OU inclusif bit à bit ^ opérateur OU exclusif bit à bit Exemples: On considère les objets de type entier i et j, et le caractère c:

i=i<<3; décale i de 3 positions binaires à gauche

j=~i; affecte à j le complément de i c=c&0x0f; applique un ET logique bit à bit entre l'octet 00001111 et l'octet c

(masque les 4 bits de poids fort de c)

e) Les opérateurs relationnels :

Il s'agit des opérateurs de comparaison qui s'appliquent sur des opérandes de type entier ou réel. Le résultat de l'expression est faux (=0) ou vrai (≠0). Cependant en langage C, il n'existe pas de type booléen de base. Aussi le résultat d'une expression est codé par un entier. Cet entier vaut 0 si le résultat de l'expression est considéré comme faux; toute autre valeur est interprétée comme donnant un résultat vrai.

Page 13: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 12

== égalité (attention aux deux signes =) != différence > >= < <=

f) Les opérateurs logiques : Les opérateurs logiques permettent de construire une expression logique à partir d'opérandes eux-mêmes logiques. Ce sont: ! opérateur unaire de négation (ne prend qu'un seul opérande) && opérateur ET logique (renvoie 0 si un des deux opérandes vaut 0) || opérateur OU logique (renvoie 0 si les deux opérandes valent 0)

g) Opérateur d'affectation : Il s'agit d'un opérateur qui copie le résultat de l'expression à droite dans la variable désignée à gauche de l'opérateur. L'opérateur est désigné par le caractère =.

identicateur_objet=expression;

Le langage C allège l'écriture de l'affectation lorsque l'expression de droite porte elle même sur la variable affectée à gauche, ceci grâce à des opérateurs d'affectation de la forme op= où op est un des opérateurs arithmétiques ou un des opérateurs de traitement de bits sauf bien sûr l'opérateur unaire de complémentation ~. Exemple:

x+=2; est équivalent à x=x+2; x*=y; est équivalent à x=x*y;

Remarques: 1) L'opérateur d'affectation ne peut être utilisé qu'avec les types de base. Le C-ANSI a

généralisé cet opérateur aux structures et aux unions, mais on ne peut pas l'utiliser avec les tableaux.

2) Attention à ne pas confondre l'opérateur d'affectation = avec l'opérateur logique de

comparaison ==. Ceci peut donner lieu à des résultats surprenants lors des évaluations d'expression; ainsi l'expression c=3 donne comme résultat vrai quelle que soit la valeur de c, alors que le résultat de l'expression c==3 dépend effectivement de la valeur de c.

Page 14: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 13

h) Autres opérateurs : • l'opérateur sizeof L'opérateur sizeof permet de récupérer sous forme d'entier la taille mémoire en nombre d'octets nécessaire pour représenter un objet ou un type donné. La syntaxe est la suivante:

sizeof(objet ou type) • l'opérateur ?: L'opérateur ?: est un opérateur ternaire qui permet de constituer une expression conditionnelle simple. La syntaxe est la suivante:

expression1 ? expression2 : expression3

L'évaluation s'effectue de la sorte: si expression1 est vraie (résultat non nul) alors c'est expression 2 qui fournit le résultat de l'expression, sinon c'est expression3. Ceci est illustré par l'exemple suivant :

valabs = (x >= 0) ? x : -x; i) Priorité des opérateurs :

Le tableau ci-après donne la priorité des opérateurs du plus prioritaire au moins prioritaire, avec pour un même niveau de priorité l'ordre d'évaluation (l'associativité) de gauche à droite ou de droite à gauche.

Opérateur Associativité Arité () [] -> . de G à D 2 ! ~ ++ -- - (type) * & sizeof

de D à G 1

* / % de G à D 2 + - de G à D 2 << >> de G à D 2 < <= > >= de G à D 2 == != de G à D 2 & de G à D 2 ^ de G à D 2 | de G à D 2 && de G à D 2 || de G à D 2 ?: de D à G 3 = += -= *= /= %= >>= <<= &= ^= |=

de D à G 2

, de G à D 2

Page 15: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 14

j) Règles de conversion : Des conversions peuvent être mises en œuvre par le compilateur lors:

− de l'évaluation des expressions − des affectations − du passage de paramètres à une fonction − du retour de l'appel d'une fonction

Les conversions appliquées sont soit des conversions implicites automatiquement appliquées, soit des conversions explicites demandées par le programmeur. • conversions implicites Les conversions automatiques transforment la représentation interne d'un opérande en une représentation plus large. Les règles appliquées sont les règles dites de promotion numérique:

− char ➙ short ➙ int ➙ long − unsigned char ➙ unsigned short ➙ unsigned int − int ➙ float ➙ double

Pour les calculs, on applique les règles de promotion en fonction du type des deux opérandes:

− si l'un est de type long, l'autre est converti en long − si l'un est de type unsigned l'autre est converti en unsigned − si l'un est de type float l'autre est converti en float − si l'un est de type double l'autre est converti en double − dans tous les autres cas, les deux opérandes sont convertis en int

• conversions explicites Le programmeur peut demander explicitement une conversion de type à l'aide d'un opérateur de conversion appelé cast operator ou opérateur de coercition de type. La syntaxe de cette demande de conversion est très simple, elle s'exprime de la sorte:

(type) expression

où type représente le type dans lequel l'expression doit être convertie. 5-4 Les structures de contrôle : Les structures de contrôle servent à traduire soit un schéma alternatif soit un schéma répétitif.

a) Les structures alternatives : • le test if Il s'agit de la traduction du schéma si (condition) alors...sinon....finsi. La syntaxe en langage C est de la forme:

Page 16: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 15

if (<expression_logique>) <bloc1> else <bloc2> Un bloc est délimité par les caractères { et }; il comprend des instructions terminée par la caractère ; la partie else étant facultative. Lorsque le bloc ne comprend qu'une seule instruction ou bien qu'il est constitué d'une structure de contrôle imbriquée, les délimiteurs { et } du bloc sont facultatifs. Attention: Ceci peut être source d'ambiguïté; en effet lorsqu'un bloc n'est pas explicitement délimité, le compilateur associe un else au dernier if. Pour lever toute ambiguïté, il faut recourir à l'usage des délimiteurs de blocs { et }. Ainsi dans l'exemple suivant, le else est rattaché au dernier if. if (<expression_logique_1>) if (<expression_logique_2>)

............; else { /* sinon de expression_logique_2 */ ............;

............; } D'autre part si pour une raison quelconque, on doit rajouter par la suite une instruction dans le bloc du premier if, il faut alors penser à mettre un délimiteur de bloc, sinon l'instruction rajoutée termine le if et le comportement du programme n'est pas du tout celui attendu. Pour toutes ces raisons, il est donc conseillé de recourir systématiquement aux délimiteurs de blocs. L'exemple précédent doit être écrit de la sorte: if (<expression_logique_1>) { if (<expression_logique_2>) {

............; } else { ............;

............; } } Remarque: Les instructions placées à l'intérieur d'un bloc sont décalées pour améliorer la lisibilité; on parle d'indentation. En ce qui concerne les ouvertures et fermetures de blocs, le style d'écriture retenu ici est celui préconisé par KERNIGHAN & RITCHIE, mais d'autres styles peuvent être utilisés.

Page 17: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 16

• le choix multiple switch Cette instruction évite le recours à un enchaînement de si alors sinon si en cascade. Elle permet de tester la valeur d'une expression entière uniquement parmi plusieurs valeurs possibles et d'effectuer un traitement approprié à chaque valeur. La syntaxe est la suivante: switch (<expression_entière>) { case valeur_1: ......; ......; case valeur_2: ......; ......; case valeur-n: ......; ......; ......; default: ......; ......; } Les expressions valeur_1 .. valeur_n doivent être des expressions constantes. Le traitement commence à la première instruction correspondant à la valeur de expression_entière lorsque celle ci est trouvée parmi les valeurs possibles valeur_i, ou bien à la première instruction correspondant à default lorsqu'elle n'est pas trouvée. S'il n'y a pas de branchement default, cela signifie qu'aucune action n'est entreprise lorsque la valeur de l'expression n'est pas trouvée. Le traitement s'arrête, soit à la première instruction de sortie break si il y en a une, soit à la fin du switch. En conséquence lorsque le programmeur veut une action particulière pour chaque valeur, il doit placer un break à la fin de la liste d'instructions associée sinon l'exécution continue dans le cas suivant. L'exemple suivant illustre ce principe.

switch (rep) { /* on teste la valeur du caractère rep */ case ' ' : nb_space++; break; case '\n': case '\0': fini=1; break; /* fini=1 pour rep ='\n' ou '\0' */ default : nb_car++; } b) Les structures répétitives :

• l'itération bornée for Il s'agit de la traduction de la boucle pour dont la forme générale est:

for(<expression_1>;<expression_2>;<expression_3>) <bloc>

Page 18: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 17

et dans laquelle: 1. expression_1 est une liste d'instructions exécutées une seule fois avant la répétitive; il

s'agira ici des initialisations 2. expression_2 est une expression logique représentant la condition de maintien dans la

boucle; cette expression est évaluée avant chaque passage donc le nombre de passage dans la boucle varie de 0 à n.

3. expression_3 est une liste d'instructions exécutées à la fin de chaque passage dans la boucle; il s'agira ici de l'itération du compteur de boucle et de l'itération d'autres variables suivant les besoins

4. expression_1 et expression_3 peuvent comprendre plusieurs instructions séparées par le caractère ,

Dans l'exemple suivant on fait la somme des 10 premiers entiers naturels

for (i=1, somme=0; i<=10; i++) somme+=i;

• la boucle while Il s'agit de la traduction de la boucle tant que et dont la forme générale est:

while(<expression >) <bloc>

et dans laquelle expression est une expression logique représentant la condition de maintien dans la boucle; cette expression étant évaluée avant chaque exécution du bloc, le nombre de passage varie de 0 à n. • la boucle do while Il s'agit de la traduction de la boucle répéter... tant que et dont la forme générale est:

do <bloc> while(<expression >)

Cependant l'écriture diffère légèrement de celle du répéter...jusqu'à puisque expression est une expression logique représentant la condition de maintien dans la boucle et non pas la condition d'arrêt; cette expression étant évaluée après chaque exécution du bloc, le nombre de passage varie de 1 à n.

Page 19: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 18

Remarques: 1) comme dans le cas du if un bloc ne comportant qu'une seule instruction peut ne pas

comprendre de délimiteurs { et }; il est cependant recommandé d'utiliser les délimiteurs pour des problèmes de maintenance

2) pour estimer la condition de maintien, il est parfois plus facile d'estimer la condition de sortie puis de complémenter cette expression

3) pour passer n fois dans une boucle, le plus simple est d'initialiser un compteur de boucle à 0 et d'écrire comme condition de maintien compteur_boucle < n

• l'instruction break L'instruction break sert à forcer la sortie dans le cas d'un switch, d'un do, d'un for et d'un while. Ceci provoque le branchement immédiat à l'instruction qui suit la structure de contrôle. Un cas d'utilisation relativement courant est la boucle infinie d'attente d'un événement, que l'on peut programmer de la sorte:

for(;;) { if (EVENEMENT) break; /* si EVENEMENT devient VRAI on sort */

}

• l'instruction continue L'instruction continue sert dans le cas d'une répétitive à forcer le branchement en fin de la boucle et donc à passer directement à l'itération suivante. Dans le cas d'un for, les expressions de fin de boucle sont effectuées avant de réévaluer la condition de maintien. Exemple:

while (i<10) { printf("\n entrez un entier naturel "); scanf("%d%*c",&n); if (n<0) continue; .......; /* traitement si n >= 0 */ .......;

}

Page 20: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 19

5-5 Les entrées-sorties : On ne s'intéresse ici qu'aux entrées au clavier et aux sorties à l'écran. Il existe à cet effet des fonctions de la bibliothèque standard étudiées plus en détail en annexe. Pour utiliser ces fonctions, il faut inclure dans le fichier source le fichier d'en-tête <stdio.h>. • saisie d'un caractère Pour saisir un caractère, il faut passer par la fonction getchar();il suffit d'affecter à une variable de type char le résultat retourné par cette fonction. Exemple:

rep=getchar(); • afficher un caractère Pour afficher un caractère, il faut passer par la fonction putchar(); l'utilisation est simple; il suffit de fournir en paramètre à cette fonction la variable de type char à afficher. Exemple:

putchar(rep); • saisie d'une variable Pour saisir une variable de type char, int ou float avec leur variante (short, long, double), il faut passer par la fonction scanf(). Cette fonction convertit des caractères saisis au clavier dans la représentation binaire associée au type de la variable en mémoire centrale. Par exemple lorsqu'on veut affecter un entier, celui-ci est représenté sur 4 octets en mémoire suivant le principe du complément à 2. L'utilisateur quant à lui saisit une suite de chiffres terminée par <Entrée>, chaque chiffre étant codé sur un octet d'après le code ASCII. La fonction scanf va alors effectuer la conversion de cette suite d'octets codés en ASCII suivant le format entier sur 4 octets en complément à 2. Etant donné que cette fonction peut effectuer plusieurs conversions car elle permet d'affecter soit un caractère soit un entier soit encore un réel (dont la représentation interne est sous forme IEEE 754), on doit lui préciser le format de conversion. Les différents format de conversion utilisables sont détaillés en annexe, mais les plus usités sont les suivants:

%c : format de conversion en caractère %s : format de conversion en chaîne de caractères %d : format de conversion en entier signé %f : format de conversion en virgule flottante simple précision %lf : format de conversion en virgule flottante double précision

Page 21: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 20

Exemple:

scanf("%d",&x); /* saisi de l'entier x */ scanf("%c",&rep); /* saisi du caractère rep */ scanf("%f",&y); /* saisi du réel y */ scanf("%s",tab); /* saisi de la chaîne tab */

On remarque que les objets de type char, int et float doivent être précédés de l'opérateur &. Nous verrons qu'il s'agit du mécanisme de passage par référence permettant effectivement à la fonction de modifier le contenu des objets passés en argument. Nous verrons également pourquoi ceci n'est pas utile dans le cas des chaînes de caractères. Remarque: La gestion des entrées-sorties par scanf peut donner lieu à des comportements qui déroutent l'utilisateur novice. En effet, l'entrée au clavier est validée par l'appui sur la touche <Entrée>. Or cette touche génère au même titre que les autres touches, un code qui est interprété comme étant le caractère '\n' ou 0A en hexadécimal. Ce caractère peut parasiter d'autres entrées par scanf puisqu'il est dans le tampon. Afin d'éviter un parasitage possible, il est conseillé de filtrer ce caractère par l'option %*c, qui signifie que l'on a saisi aussi un octet mais qui ne sera affecté à rien. Ainsi la saisie d'un entier devient:

scanf("%d%*c",&x); • affichage d'une variable Pour afficher une variable de type char, int ou float avec leur variante (short, long, double), il faut passer par la fonction printf(). Cette fonction convertit en caractères affichés à l'écran le contenu d'une variable dont la représentation en mémoire centrale dépend du type de la variable. Comme dans le cas de scanf(), cette fonction peut effectuer plusieurs conversions précisées par des formats. Ces formats sont les mêmes que pour scanf() et ils sont détaillés en annexe. L'exemple suivant illustre le principe de fonctionnement de printf(). Ce qui doit être affiché est placé entre les caractères " ". On précise dans cette chaîne le texte à afficher et chaque fois qu'il doit y avoir l'affichage d'une variable, on indique non pas la variable mais le format de conversion. Les variables sont ensuite passées en paramètres et dans l'ordre; il est possible d'utiliser également les caractères spéciaux comme \n qui sert de saut de ligne.. Ici on veut afficher un entier et un réel.

printf("\n\nla valeur de x est %d et celle de y est %f : ",x,y);

Page 22: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 21

• saisie au vol d'un caractère Pour saisir au vol un caractère, c'est à dire sans avoir à taper sur la touche <Entrée>, il faut passer par une fonction propre au département et qui s'appelle kbget(). Pour utiliser cette fonction, vous devez inclure le header <iutio.h> situé dans le répertoire /usr/local/include; cette fonction retourne le caractère frappé au clavier dès lors que l'utilisateur a appuyer sur une touche (cf. Annexe B). 6 Le préprocesseur C: Tout texte source écrit en langage C est d'abord analysé par le préprocesseur avant d'être traduit par le compilateur. Cette phase est automatiquement exécutée lorsqu'on invoque le compilateur (et donc lorsqu'on passe par la procédure de compilation ccomp). Le rôle du préprocesseur se limite à une simple manipulation du texte du fichier source, c'est à dire qu'en sortie du préprocesseur le fichier fourni reste du texte. On peut d'ailleurs demander uniquement le passage du préprocesseur avec l'option -E du compilateur C du GNU ou du compilateur HP-UX. Il est également possible d'invoquer directement le préprocesseur par la commande suivante:

/usr/ccs/lbin/cpp nom_fichier_source.c

Les directives passées au préprocesseur commencent toujours par le caractère # et elles ne comprennent pas le caractère ; qui marque la fin d'une instruction C. Le rôle du préprocesseur est de permettre: • l'inclusion des fichiers d'en-tête .h: ce sont des fichiers texte qui comprennent des

définitions de types, de constantes et le prototypage de fonctions; la directive d'inclusion est #include <xxxxx.h> ou encore #include "xxxxx.h" lorsque le fichier d'en-tête est situé dans le répertoire courant. Le préprocesseur inclut alors à l'emplacement de la directive le contenu du fichier .h

• la définition de constantes: la directive #define NOM_CONST valeur_const indique au préprocesseur qu'il doit substituer dans le texte source C toute occurrence de NOM_CONST par valeur_const

• la définition de macroinstructions: il s'agit de définitions paramétrées grâce à la directive #define NOM_MACRO(paramètre_1,...paramètre_n) expression. Le préprocesseur effectue là aussi la substitution à chaque occurrence dans le texte de NOM_MACRO(paramètres) par l'expression définie; comme il s'agit de substitution de texte, il faut avoir recours au parenthésage pour forcer les priorités

• la compilation conditionnelle: il existe des directives qui permettent de sélectionner suivant une condition certaines parties du texte source. Cet aspect est traité au chapitre 6 sur la programmation modulaire

Page 23: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 22

Exemples:

#include<stdio.h> /* inclusion du fichier d'en-tête stdio.h situé dans /usr/include */

#define MAX 10 /* définition de la constante symbolique MAX qui vaut 10 */

#define CARRE(x) ((x)*(x)) /* définition de la macro CARRE; le parenthésage permet d'invoquer la macro avec des expression comme CARRE(a+b) */

Page 24: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 23

Page 25: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 24

CHAPITRE 2 - LES TYPES CONSTRUITS 1 Le type énumération: Le type énuméré est un moyen pratique de définir une variable qui peut prendre une valeur parmi un ensemble d'entiers constants appelés constantes énumérées. La valeur des constantes énumérées peut être fixée par défaut ou bien être définie par l'utilisateur d'après le principe suivant :

• chaque valeur peut être imposée par le programmeur • lorsqu'une valeur n'est pas imposée, elle se déduit de la précédente par

incrémentation de 1 • lorsqu'aucune valeur n'est imposée, la valeur de la première constante énumérée est 0, les autres se déduisant par incréments successifs

La définition d'une énumération se fait grâce au mot réservé enum suivant la syntaxe:

enum identificateur_enumeration{liste_constantes_enumerees}; Exemples:

enum bool {FAUX,VRAI}; /* définition de la variable énumérée bool qui ne peut prendre que */ /* deux valeurs FAUX (=0) ou VRAI (=1) */

enum jour {LUNDI=1,MARDI,MERCREDI,JEUDI,VENDREDI,SAMEDI,DIMANCHE}; /* définition de la variable énumérée jour qui ne peut prendre que */ /* sept valeurs LUNDI (=1) ou MARDI (=2) ou MERCREDI (=3) etc */

enum statut {ERREUR=-1, OK=1}; /* définition de la variable énumérée statut qui ne peut prendre */ /* que deux valeurs possibles ERREUR (=-1) ou OK (=1) */

Il est souvent pratique de construire un type pour une sorte d'énumération. On passe pour cela par la directive typedef suivant la syntaxe:

typedef enum {liste_constantes_enumerees} NOUVEAU_TYPE; Comme le montre l'exemple suivant, le type booléen n'existant pas en C, il est possible de définir un type que l'on appelle ici LOGIQUE et qui ne prendra que la valeur VRAI ou la valeur FAUX.

typedef enum {FAUX, VRAI} LOGIQUE; On peut ensuite à tout moment définir une variable du nouveau type LOGIQUE, comme par exemple:

LOGIQUE fini;

Page 26: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 25

2 Le type tableau: 2-1 Définition: Un tableau est une structure de données qui regroupe une suite finie d'objets de même type disposés de façon contiguë en mémoire centrale. Les éléments du tableau sont formés par ces objets qui sont repérées par un rang. Le rang d'un élément est un entier ou une expression. Un tableau est caractérisé par sa dimension qui peut être un entier N quelconque, mais généralement on ne considère que des tableaux de dimension 1 (vecteur) ou 2, voire 3 (matrice). Nous ne nous intéresserons ici dans un premier temps qu'aux tableaux à une dimension. 2-2 Déclaration et initialisation d'un tableau: En langage C, on déclare un tableau en précisant l'identificateur du tableau, le type des variables qui constituent ses éléments et la taille du tableau.

type_des_éléments identificateur_tableau[nombre_d'éléments]; Exemple:

int tabl[10]; /*déclare un tableau de 10 entiers qui s'appelle tabl */

Comme toutes les variables en langage C, un tableau peut être initialisé à sa définition, par une liste d'éléments compris entre accolades. Les éléments de la liste sont affectés dans l'ordre aux éléments du tableau. Le nombre d'éléments de la liste doit bien sûr être inférieur ou égal au nombre d'éléments que peut contenir le tableau. Les autres éléments sont mis à zéro et il n'est pas possible d'initialiser un élément sans avoir initialisé les précédents. Dans le cas particulier des tableaux de caractères, ceux-ci peuvent être initialisés caractère par caractère, ou bien par une constante de type chaîne. Le nombre d'éléments du tableau peut être omis lorsque:

• le tableau est un paramètre formel d'une fonction • le tableau est une variable de classe de mémorisation extern (voir chapitre 6) • le tableau est explicitement initialisé lors de sa définition

Exemples:

int mois[] = {1,2,3,4,5,6,7,8,9,10,11,12}; char tab[] = {‘a’,’l’,’a’,’i’,’n’,’\0’} ; char tab[] = "langage" le caractère fin de chaîne \0 est alors

rajouté

Page 27: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 26

2-3 Manipulation des éléments d'un tableau: On atteint un élément d'un tableau par son rang. Dans le cas d'un tableau monodimensionnel, (vecteur), l'élément de rang i (i étant un entier ou le résultat entier d'une expression) est atteint par: identificateur_tableau[i] avec i ∈ [0..N-1] N étant la taille du tableau; En algorithmique, on considère souvent que le premier élément d'un tableau est de rang 1 alors qu'en langage C, il est de rang 0. Nous verrons par la suite pourquoi. Les éléments sont donc comptés de 0 à N-1. Par exemple, avec le tableau tabl de 10 entiers déclaré précédemment, on a:

tabl[0] /* premier élément */ tabl[1] /*deuxième élément */ ................. tabl[9] /* dixième élément */

On peut manipuler l'élément d'un tableau comme toute variable, c'est à dire:

• l'affecter par une valeur ou par le résultat d'une expression donnant un type identique à celui de l'élément

• lui appliquer tous les opérateurs compatibles avec son type Exemples:

for(i=nb_elem;i>k;t[i]=t[i-1],i--); /* décalage à droite des éléments du tableau depuis le rang k */ t[i]=x; /* affectation de x à l'élément de rang i */ t[0]++; /* incrémentation du premier élément du tableau t */

2-4 Tableau à deux dimensions: En langage C, un tableau à deux dimensions est en fait un tableau à une dimension dont chaque élément est lui même un tableau. Par exemple si on veut disposer d’une matrice de caractères de 10 lignes par 20 colonnes, il faut déclarer :

char tab[10][20]; /* 10 lignes et 20 colonnes */ On accède alors à un élément en précisant le numéro de ligne et le numéro de colonne. Par exemple, si on considère la matrice tab déclarée précédemment, tab[2][5] représente le cinquième élément de la deuxième ligne, car tab[2][5] correspond aussi à tab[2*20+5] en linéarisant la matrice.

Page 28: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 27

Tout comme dans le cas d'un tableau monodimentionnel, on peut initialiser une matrice en donnant la liste des valeurs par ligne et colonne, et appliquer sur chaque élément les opérateurs d'affectation et ceux compatibles avec le type des éléments de la matrice. Exemple: On détermine les éléments de la matrice D de m lignes et p colonnes résultat du produit de la matrice A de m lignes n colonnes par la matrice B de n lignes p colonnes;

for(i=0;i<m;i++) { for (j=0;j<p;j++) { for(somme=0,k=0;k<n;k++) { somme+=A[i][k] * B[k][j]; } D[i][j]=somme; } }

3 Le type structure: 3-1 Définition d’une structure: Une structure est un agrégat d'éléments de type différents. Chaque élément peut avoir les types suivants: • types de base (caractère, entier, réel) • type pointeur • type structure ou union • type tableau • type construit par le développeur sauf une fonction (mais peut être un pointeur sur

fonction) Si la structure contient une structure, cela ne peut être celle en cours de définition, mais en revanche cela peut être un pointeur sur celle ci. Les éléments qui constituent une structure sont appelés membres de la structure.

Page 29: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 28

3-2 Construction d'une structure: La définition en C d'une structure se fait à l'aide du mot réservé struct. La définition commence par le caractère { et se termine par le caractère }. Dans l'exemple suivant

struct individu { char nom[20]; char prenom[15]; int age; char sexe; } etudiant;

individu représente un descripteur de type struct qui contient les membres (appelés aussi parfois champs) nom, prénom, age et sexe. En revanche etudiant représente une instance de la structure, c'est à dire un objet de type struct individu. On peut initialiser une variable de type structure au moment de sa définition en la faisant suivre par une liste d'expression constante correspondant à chaque membre de la structure . Exemple:

struct individu prof = {"deloin","alain",36,'m'}; Une autre façon de procéder pour construire une structure est de définir un nouveau type grâce à typedef. Ceci permet de ne pas avoir à indiquer le type struct lorsqu'on définit une variable correspondant à un descripteur de structure. Ainsi dans l'exemple suivant

typedef struct individu { char nom[20]; char prenom[15]; int age; char sexe; } INDIVIDU;

individu est le descripteur d'une structure contenant les membres nom, prenom, age et sexe alors que INDIVIDU est un type synonyme de struct individu. Ainsi pour déclarer les variables etudiant et prof de type structure suivant le descripteur struct il suffit maintenant d'écrire:

INDIVIDU prof, etudiant; Ceci est équivalent à:

struct individu prof, etudiant; On voit donc bien que individu n'est qu'un descripteur de structure et non pas un type. Le type d'une structure est donc défini par struct descripteur, comme c'est le cas ici avec struct individu. On constatera par la même occasion la souplesse apportée par le mécanisme de construction de type synonyme grâce à typedef.

Page 30: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 29

3-3 Accès aux membres d'une structure: On accède aux membres d'une structure grâce à l'opérateur . qui permet d'associer le nom du membre au nom d'une structure. Ainsi pour atteindre le membre d'une structure, il faut indiquer le nom de la variable structure suivi du nom de la variable membre d'après la syntaxe structure.membre. Exemple:

struct individu { char nom[20]; char prenom[15]; int age; char sexe; } etudiant,prof; strcpy(etudiant.nom,"dugommier"); strcpy(etudiant.prenom,"robert"); etudiant.age=20; etudiant.sexe='m';

On peut utiliser le membre d'une structure comme une variable quelconque, par exemple:

printf("%s",etudiant.nom); if (etudiant.age > 65 ) ?????;

3-4 Structures imbriquées: Une structure peut comprendre un membre de type structure pourvu que le type en question ait été défini préalablement. Exemple:

struct adresse { int numero; char rue[25]; }; struct individu { char nom[20]; char prenom[15]; struct adresse adr; int age; char sexe; };

On peut initialiser au moment de la déclaration une structure imbriquée en respectant l'ordre de la construction lors de l'affectation des expressions constantes. Ainsi avec la déclaration précédente:

struct individu prof = {"deloin","alain",10,"avenue joffre",36,'m'};

Page 31: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 30

Pour atteindre le membre d'une structure imbriquée dans une structure, on répète le mécanisme d'accès par l'opérateur . en respectant la hiérarchie de construction. L'exemple suivant s'applique à la construction précédente.

printf("%s %s ",prof.nom,prof.prenom); printf("%d %s ",prof.adr.numero,prof.adr.rue); printf("%d %c ",prof.age,prof.sexe);

3-5 Affectation d’une structure: Pour affecter une structure, on peut soit affecter les membres les uns après les autres, soit (et ceci depuis la norme ANSI) affecter globalement une structure par une autre structure pourvu qu'elle soit de même type. Il s’agit alors d’une recopie en mémoire octet par octet définie par la taille identique des deux structures. C’est un mécanisme qui permet en plus de s’affranchir du problème du choix de l’opérateur d’affectation en fonction des types des membres de la structure. Exemple :

struct individu etudiant_2annee, etudiant_1annee ; ....................... etudiant_2annee=etudiant_1annee ;

4 Le type union: 4-1 Définition d’une union: Une variable de type union est une variable qui peut contenir à un moment donné des objets de type et de taille différents. La déclaration est identique à celle d'une structure sauf que le mot union remplace le mot struct. Mais contrairement à une structure, l'allocation mémoire est faite ici en recouvrement, car les différents membres sont une description différente d'une même zone mémoire. L'union est très pratique puisqu'elle permet de définir une variable qui peut contenir toutes sortes de type. Exemple:

union bidon { int code; float valeur; char * libellé; } zone-trav ;

A un moment donné la variable zone-trav de type union bidon peut contenir un entier un réel ou une chaîne. C’est au programmeur de savoir ce qu’il manipule.

Page 32: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 31

4-2 Affectation et accès aux membres d’une union: On accède aux membres de l'union de la même façon qu’une structure. Pour accéder à un membre on utilise la syntaxe

variable_union.membre;

L'affectation se fait également comme dans le cas d'une structure. On peut imbriquer des unions dans des structures. On accède alors à la variable union comme un membre de la structure. Exemple: On considère les définitions suivantes:

typedef struct { char nom[31]; char prenom[31]; } E1; typedef struct { char titre[56]; int date; } E2; typedef struct { int code; union { E1 e1; E2 e2; } enreg; } BUFF; BUFF buff;

On peut alors écrire par exemple:

scanf("%s",buff.enreg.e1.nom); printf("\n le nom entre vaut : %s ",buff.enreg.e1.nom);

Page 33: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 32

5 Le type pointeur: 5-1 Notion de pointeur: Les pointeurs représentent une classe d'objets capables de contenir des adresses d'autres variables en mémoire. Une adresse en mémoire centrale a un format dépendant de l'implémentation, mais sur une même machine, elles ont toutes le même format. Cependant en langage C, un pointeur reste une variable typée, en ce sens qu'un pointeur est associé au type de l'objet dont il contient l'adresse. Cela signifie qu'un pointeur ne peut contenir l'adresse que d'objets d'un type donné, et pour lequel il a été défini. Il est d'usage de dire que le pointeur pointe sur un objet pour dire qu'il contient l'adresse de cet objet, et d'appeler l'objet lui-même objet pointé. On pourra donc atteindre l'objet par son identificateur ou par le pointeur qui contient l'adresse de cet objet. Lorsqu'on atteint un objet à travers un pointeur, on parle d'indirection. Pointeur ptx Objet pointé x Adresse de x Contenu de x

5-2 Définition: Un pointeur en C étant en fait un pointeur sur un type donné, la définition se fait en précisant le type des objets sur lequel le pointeur pourra pointer. La syntaxe utilisée est la suivante:

type_objet * identificateur_pointeur; L'opérateur utilisé pour indiquer un type pointeur est l'opérateur unaire *; il permet de distinguer la définition d'un objet de type type_objet et celle d'un pointeur sur un objet de type type_objet. Exemple:

int * pti; /* pti est un pointeur sur entier; il ne pourra pointer que sur des

objets de type int */ float *ptf; /* ptf est un pointeur sur réel; il ne pourra pointer que sur des

objets de type float */

Page 34: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 33

5-3 Affectation à un pointeur: La définition d'un pointeur entraîne la réservation mémoire nécessaire pour représenter le pointeur. Il s'agit en ce sens d'un objet comme un autre. Mais la définition n'implique pas l'initialisation du pointeur. Il faut donc pouvoir affecter à un pointeur l'adresse d'un objet d'un type correspondant au type pointé. Ceci se fait grâce à l'opérateur unaire & (adresse de) qui placé devant l'identificateur d'un objet, permet de récupérer son adresse. Dans le cas particulier où l'on ne veut pas faire pointer un pointeur sur une adresse valide mais simplement indiquer que le pointeur ne contient pas d'adresse, on lui affecte la valeur spéciale NULL prédéfinie dans le fichier d'en-tête <stdio.h>. Cette constante symbolique qui vaut le zéro binaire permet de signifier lorsqu'elle est affectée à un pointeur, que ce dernier ne pointe sur rien. Exemple:

int i; int *pti, *ptj; /* définition des pointeurs sur entier pti et ptj */ pti = &i; /* on affecte à pti l'adresse de l'objet i; donc pti pointe sur i */ ptj = NULL; /* ptj ne pointe sur aucun objet */

Comme pour tout autre objet, on peut initialiser un pointeur au moment de sa définition. Il suffit pour cela que l'objet dont au veut affecter l'adresse au pointeur soit préalablement défini. Il est également possible d'affecter un pointeur par le contenu d'un autre pointeur, pourvu que les deux pointeurs soient de même type. Exemple:

int i, j; /* définition des entiers i et j */ int *pti=&i; /* définition et initialisation du pointeur pti */ int *ptj; /* définition du pointeur ptj */ ptj = pti; /* on affecte à ptj le contenu de pti c'est à dire l'adresse de i donc ptj pointe sur i */

Page 35: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 34

5-4 Opérations sur les pointeurs:

• arithmétique sur les pointeurs Un pointeur représente pour le programmeur le moyen d'accéder à une zone mémoire organisée comme une suite consécutives d'éléments de même type, puis ensuite de s'y déplacer en effectuant des calculs d'adresse grâce aux opérations suivantes:

− addition ou soustraction d'un entier à un pointeur − soustraction entre deux pointeurs de même type

Il est important de noter que pour effectuer ces calculs d'adresse, la zone doit être vue comme un tableau (collection consécutive d'objets de même type). Le C tient donc compte dans toutes ces opérations, du type de l'objet pointé. Ainsi, un entier n ajouté à un pointeur est d'abord multiplié par la taille du type pointé, puis ajouté à l'adresse contenu dans le pointeur. Ceci traduit un déplacement de n nombres d'éléments de type pointé. Il en est de même avec la soustraction d'un entier à un pointeur. L'exemple suivant (dans lequel pti est défini comme int *pti) illustre ce principe

pti + 3 est traduit en pti + 3 * sizeof(int) pti++ est traduit en pti + 1 * sizeof(int)

La soustraction de deux pointeurs sur un même type donne le nombre d'éléments de ce type compris entre les deux adresses contenues dans les pointeurs.

• comparaison de pointeurs Il est possible de comparer deux pointeurs, si il s'agit de pointeurs sur un type identique. On peut également comparer un pointeur à la constante symbolique NULL.

• opérateur de coercition sur les pointeurs Parfois, le programmeur a besoin d'affecter l'adresse d'un objet d'un type donné à un pointeur sur type différent (exemple: affectation d'un pointeur par l'appel à une fonction qui retourne un pointeur sur un autre type). Dans ce cas, il faut préciser une conversion explicite grâce à un cast operator (cf. chapitre 1). Dans l'exemple suivant, on veut pour une raison quelconque affecter le contenu du pointeur sur caractère ptc au pointeur sur entier pti.

char *ptc; int *pti; ............; pti=(int *) ptc;

Page 36: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 35

5-5 Comment atteindre l'objet pointé? Pour atteindre un objet via un pointeur qui contient l'adresse de cet objet, on utilise l'opérateur * d'indirection. La syntaxe est toujours la même quel que soit le type pointé: * ptr désigne l'objet pointé par le pointeur ptr . Exemples:

int i, j; /* définition des entiers i et j */ int *pti, *ptj; /* définition des pointeurs sur entiers pti et ptj */ pti=&i; /* pti pointe maintenant sur i */ ptj=&j; /* ptj pointe maintenant sur j */ *pti=3; /* (*pti) désigne alors i, donc i reçoit 3 */ (*pti)++; /* équivalent à i++; attention ceci est différent de *(pti++) qui consite a incrémenter le pointeur de 1*sizeof(int) puis de référencer la nouvelle adresse obtenue */

*ptj= *pti; /* on affecte l'objet pointé par pti à l'objet pointé par ptj; ce qui revient à écrire j = i */

ptj=pti; /* on affecte le contenu de pti à ptj donc ptj pointe sur i; */

5-6 Pointeurs et tableaux: L'arithmétique des pointeurs en C permet de traiter toute zone mémoire organisée comme une suite consécutives d'objets de même type, ce qui revient à considérer la zone traitée comme un tableau. Le C aborde donc de façon uniforme les tableaux et les pointeurs. Le mécanisme de l'indexation dans un tableau est identique au calcul d'adresses par des pointeurs. Ainsi lorsqu'on déclare un tableau tab de n éléments d'un certain type T, tab représente en fait l'adresse du début de la zone mémoire allouée pour contenir n éléments du type T. Dès lors, l'accès à un élément par l'écriture tab[i] est strictement équivalente à *(tab+i) ce qui d'après l'arithmétique des pointeurs signifie: élément contenu à l'adresse tab augmentée de i fois la taille de représentation d'un élément de type T. Ceci permet de comprendre pourquoi en C, l'accès à un premier élément se fait par l'indice 0. En effet comme tab|0] est équivalent à *(tab), on retrouve bien le contenu du premier élément du tableau.

Page 37: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 36

Il y a donc similitude entre les pointeurs et les tableaux, comme le montre l'exemple suivant:

int x; int *pti; /* définition d'un pointeur sur entier */ int tab[5]; /* définition d'un tableau de 5 entiers */ pti=tab; /* on fait pointer pti sur le tableau tab */ x= *pti; /* affecte à x le premier élément de tab; ceci est

équivalent à faire x = tab[0]; pti est inchangé */ x= *(pti+2); /* affecte à x le troisième élément de tab; ceci est

équivalent à x = tab[2] ; pti est inchangé */

pti++; /* fait pointer pti sur le deuxième élément de tab; ceci est équivalent à pti = pti + 1 * sizeof(int) */

Attention: Il existe cependant quelques différences entre un tableau et un pointeur:

• l'identificateur d'un tableau correspond en fait à un pointeur constant; ainsi dans les définitions de l'exemple précédent on ne pas faire tab++ ou bien affecter tab, alors qu'un pointeur est une variable

• lorsqu'on définit un tableau, il y a allocation mémoire de la zone contenant les éléments du tableau

• lorsqu'on définit un pointeur, il y a allocation mémoire de la zone qui va contenir la valeur du pointeur (comme pour toute définition d'un objet), mais il n'y a absolument pas allocation de l'objet pointé; ce dernier devra être par ailleurs défini soit par une définition explicite, soit par le mécanisme d'allocation dynamique

5-7 Pointeurs et allocation dynamique de mémoire: Tout objet qui va être manipulé par le programme doit être préalablement défini. La définition consiste a indiquer pour chaque objet, son type, son identificateur et éventuellement sa valeur d'initialisation. La réservation de la place mémoire pour représenter les objets définis se fait au moment de la traduction du code source. En revanche, il est parfois utile de pouvoir allouer de la mémoire à un objet au moment de l'exécution du programme. Dans ce cas, c'est au développeur de prévoir dans son programme l'appel à un mécanisme d'allocation mémoire qui retournera un pointeur sur la zone mémoire de la taille demandée. De même, c'est toujours au développeur de penser à libérer dans son programme la zone mémoire lorsqu'il n'en a plus besoin.

Page 38: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 37

Les deux mécanisme d'allocation mémoire sont donc fondamentalement différents, l'un est fait à la traduction, l'autre à l'exécution. Sous Unix, un programme exécutable comprend en fait trois régions:

• un zone code qui représente les instructions et les constantes; il s'agit d'un invariant • une zone données qui représente les objets manipulés par les instructions (variables

globales ou variables locales rémanentes, c'est à dire qui conservent la valeur qui leur a été affectée lors du dernier appel)

• une zone pile qui permet le mécanisme de passage des paramètres à une fonction, et qui contient les variables locales à la fonction; dans cette zone on trouve également le tas qui permet de gérer les objets alloués dynamiquement à l'exécution

Pour allouer de la mémoire dynamiquement en C, on passe par les services de la fonction malloc. On doit lui indiquer le nombre d'octets mémoire voulus, et la fonction retourne un pointeur sur cette zone. Comme en C les pointeurs sont typés, la fonction retourne un pointeur générique noté void *, indiquant par là qu'il s'agit d'un pointeur pouvant pointer sur n'importe quel type de donnée. Cette fonction est prototypée (cf. le chapitre 3) de la sorte:

void * malloc(unsigned n); Lorsque l'allocation échoue, la fonction retourne NULL. Pour assurer la comptabilité des types, on passe par l'opérateur de coercition comme le montre l'exemple suivant:

int *pti; /*définition d'un pointeur sur entier */ char *ptc; /*définition d'un pointeur sur caractères */ pti=(int *)malloc(5*sizeof(int)); /* pti pointe sur une zone pouvant contenir 5 entiers */ ptc=(char *)malloc(25); /* ptc pointe sur une zone pouvant contenir 25 caractères */

Pour libérer la zone mémoire allouée, on passe par les services de la fonction free. On lui indique l'adresse de début de la zone à libérer. L'appel a free ne modifie pas le contenu du pointeur, en particulier il n'est pas remis à la valeur NULL.

free(pti); /* libère la place mémoire obtenue par allocation dynamique pointée par pti */ free(ptc); /* libère la place mémoire obtenue par allocation dynamique pointée par ptc */

Page 39: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 38

5-8 Pointeurs et structures: On peut définir des pointeurs sur des variables de type structure. On accède alors à un membre la structure suivant la syntaxe:

pointeur_sur_la_structure -> nom_membre Le signe -> est constitué du caractère - suivi du caractère >. Exemple:

struct individu { char nom[20]; char prénom[15]; int age; char sexe; } * ptst;

............. ............. ptst=(struct individu *) malloc(sizeof(struct individu));

scanf("%s",ptst->nom); scanf("%d",&ptst->age);

On remarquera que la définition d'un pointeur n'engendre pas la définition de l'objet pointé, comme cela a déjà été souligné, d'où l'appel à la fonction malloc pour allouer dynamiquement une zone mémoire capable de contenir la taille d'une structure de type struct individu et accessible par le pointeur ptst. Il est également possible de définir une structure contenant un pointeur sur la structure que l’on est en train de définir. Ceci est utilisé dans les structures de données complexes comme les listes chaînées ou les arbres. L'exemple suivant illustre ce mécanisme: typedef struct liste_individu {

struct individu corps ; struct liste_individu *suivant; } * LISTE ; /* LISTE est un type pointeur sur struct liste_individu */

On définit ici un type LISTE qui pointera sur un type struct liste_individu comprenant un membre appelé corps de type struct individu et un membre appelé suivant qui pointera lui aussi sur un objet de type struct liste_individu (donc de type LISTE) ou sur rien (NULL).

Page 40: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 39

5-9 Utilisation des pointeurs: En langage C les pointeurs sont largement utilisés. Ils permettent notamment:

• de construire des structures de données complexes (liste chaînée, arbre, etc.) • d'accéder à une zone mémoire allouée dynamiquement • de traiter le mécanisme d'indexation pour accéder à un tableau ou une zone

mémoire contiguë • d'implémenter le mécanisme de passage de paramètres par référence • d'appeler des fonctions

Les deux derniers cas d'application cités sont abordés respectivement dans le chapitre 3 et dans le chapitre 5. 6 Le cas des chaînes de caractères: Il n'existe pas en C de type primitif chaîne de caractères. Une chaîne est vue en fait comme un tableau de caractères, la fin de chaîne étant délimitée par le caractère spécial '\0' qui est en fait un octet au zéro binaire. Un type chaîne est donc similaire au type char *. 6-1 Définition d'une chaîne: Pour définir une chaîne, le développeur a la possibilité soit de passer par un tableau de caractère, soit par un pointeur de type char *, mais dans ce cas il faut allouer dynamiquement une zone mémoire pour contenir la chaîne. Comme la chaîne doit absolument se terminer par '\0', il faut penser à allouer un octet de plus pour contenir le caractère fin de chaîne.

char nom[21]; /* chaîne de 20 caractères */ char * mesg /* pointeur sur char */ .............. .............. mesg=(char *)malloc(21); /* allocation d'une zone mémoire pouvant recevoir au plus une chaîne de 20 caractères */

Comme pour toutes les autres définitions, il est possible d'initialiser une chaîne au moment de la définition. En C, une constante chaîne est délimitée par les caractères " ". La représentation interne de la constante est une zone mémoire de taille suffisante pour contenir la chaîne placée entre les guillemets terminée par le caractère '\0'. Elle est donc de type pointeur sur caractère char *. L'allocation mémoire pour contenir la constante étant faite à la traduction, le pointeur qui contient son adresse est un pointeur constant. Lors de l'affectation, il faut donc faire attention aux déclarations car certaines sont illicites.

Page 41: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 40

Exemple:

char nom[20]={'e','s','s','a','i','\0'}; /* nom tableau de 20 caractères initialisé avec la chaîne essai */ char msg[]="essai"; /* msg tableau de 6 caractères initialisé avec la chaîne essai */ char *pta="test"; /* pta pointeur sur une zone mémoire contenant la chaîne test */ pta=msg; /* pta pointe maintenant sur la chaîne contenue dans le tableau de caractères msg */ msg="test"; /* illicite: en effet msg est défini en tant que pointeur constant on ne peut pas l'affecter par un autre pointeur ici le pointeur qui pointe sur la chaîne test */

Il faut également faire toujours attention que la zone mémoire contient au moins une position de plus que la chaîne afin de recevoir le caractère '\0'. 6-2 Les opérations sur les chaînes: Qu'une chaîne soit traitée à l'aide d'un tableau de caractères, ou bien par allocation dynamique d'une zone mémoire accédée par un pointeur, elle est vue comme une suite d'octets de longueur variable terminée par '\0'. Comme une chaîne n'est pas un type de base en C, il n'existe pas d'opérateurs spécifiques au traitement de chaînes; il faut passer par des fonctions de la bibliothèque standard qui sont toutes conformes à cette convention de représentation d'une chaîne quelconque, ce qui permet de souligner une fois encore l'importance du caractère délimiteur de fin de chaîne. Il existe un grand nombre de fonctions de traitement de chaîne de caractères, aussi seules les principales sont évoquées ici. Pour plus de détail se reporter à l'annexe sur la bibliothèque standard.

• saisie Pour saisir une chaîne on utilise les fonctions gets ou scanf de la bibliothèque standard. Dans le cas de scanf, le format à préciser est %s, et l'argument à fournir doit être une référence (voir le chapitre sur les fonctions); comme une chaîne est de type char *, ceci explique pourquoi on ne fait pas appel à l'opérateur & pour les chaînes. Exemple:

char ch[11]; gets(ch); scanf("%s",ch);

Page 42: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 41

Si la chaîne à saisir comprend plusieurs champs séparé d'un espace, la fonction scanf ne décode que le premier champ. Une autre façon de procéder est d'affecter la chaîne caractère par caractère en utilisant la fonction de saisie au vol kbget propre au département. Cela laisse au programmeur la possibilité d'écrire la fonction de saisie qu'il veut exactement. L'exemple suivant montre la traduction simple d'une fonction de saisie de chaîne; la saisie s'arrête lorsque l'utilisateur a appuyé sur la touche Entrée ou bien lorsque le nombre de caractères saisis est égal à la taille maximale de la chaîne moins une position réservée au caractère '\0'. # define TAILLE 10

char ch[TAILLE]; char c; int i; .............. for(i=0;i<TAILLE-1 && (c=kbget())!=0x0a;i++) { ch[i]=c; putchar(c); /* car kbget fonctionne sans echo */ } ch[i]='\0';

• affichage L'affichage d'une chaîne se fait soit caractère par caractère à l'aide de la fonction putchar et ce tant que le caractère à afficher n'est pas le délimiteur '\0', ou bien globalement avec la fonction puts ou la fonction printf avec le format %s. Exemple:

/* avec les mêmes définitions que dans l'exemple précédent */ for(i=0;i<TAILLE && ch[i]!='\0';i++) { putchar(ch[i]); } /* on remarquera que l'affichage vérifie qu'il n'y a pas dépassement du tableau ch */ printf("la chaine vaut : %s \n",ch); /* affichage par printf */ puts(ch); /* affichage par puts */

• affectation Pour affecter à une chaîne le contenu d'une autre chaîne, on utilise les fonction strcpy ou sa variante strncpy dont le comportement est expliqué en annexe

Page 43: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 42

• comparaison

On ne peut pas utiliser l'opérateur de comparaison ==. Dans le cas des chaînes, il faut passer par la fonction strcmp ou la fonction strncmp de la bibliothèque standard (cf. annexe).

• concaténation De même pour concaténer une chaîne à la fin d'une autre chaîne, il faut passer par les fonctions strcat et sa variante strncat (cf. annexe).

• récupération de la longueur d'une chaîne On fait appel à la fonction strlen qui renvoie un entier représentant le nombre de caractères de la chaîne sans compter le délimiteur '\0'(cf. annexe).

Page 44: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 43

Page 45: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 44

CHAPITRE 3 - LES FONCTIONS 1 Rôle d’une fonction : La notion de fonction est fondamentale dans l'esprit d'analyse descendante d'un problème de programmation. Elle correspond au découpage logique par le programmeur du problème à traiter, et représente un ensemble du code du programme qui réalise une tâche particulière voire répétitive, sur des objets de type divers. Une fonction est donc un sous-programme indépendant qui reçoit éventuellement des paramètres (ou encore des arguments) au moment de son appel, et qui retourne une valeur au programme appelant. Intérêts des fonctions - découper un gros programme en unités plus petites; - isoler des traitements particuliers; - éviter de réécrire le même traitement; - améliorer la lisibilité des programmes; -accroître la portabilité; D'autre part, les fonctions dont la vocation est d'être réutilisées par d'autres programmes peuvent être compilées dans des modules "objet" archivés dans des bibliothèques courantes accessibles à tous les programmeurs, ce qui évite de les réécrire. En langage C, un programme est constitué d'une suite de fonctions qui ne peuvent être emboîtées. La fonction main est une fonction particulière qui constitue le point d'entrée du programme. Nous verrons par la suite qu'il est possible également d'éclater un programme C en plusieurs fichiers et de faire de la compilation séparée. 2 Prototypage d'une fonction: Une fonction doit être déclarée préalablement à son utilisation au même titre que les variables. Le prototypage (ou déclaration) d'une fonction vise donc à spécifier les caractéristiques de la fonction, c'est à dire le type des paramètres qui vont être passés à la fonction ainsi que le type de la valeur retournée par la fonction. Elle s'effectue en tête du fichier source.

Page 46: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 45

Exemple: #include <stdio.h> int i,j; char rep; /* déclaration de la fonction carre à laquelle on va passer un entier et qui va retourner un double */ double carre(int); Le prototypage d'une fonction permet aux compilateurs C-ANSI d'effectuer un contrôle de compatibilité de type des arguments passés au moment de l'appel de la fonction. Remarques : 1. avec les anciens compilateurs C, on ne peut faire qu'une déclaration sans prototypage.

Ainsi, dans l'exemple précédent la fonction carré aurait été déclarée de la sorte double carre(); /* il n'y a donc pas de contrôle de type sur les arguments passés */ 2. pour un programme, il est possible de réunir dans un même fichier appelé header ou

fichier d'en-tête les prototypes des fonctions qui sont définies dans ce programme. Ce fichier est alors importé dans tous les modules (y compris lui-même) qui appellent ces fonctions par la directive :

# include <nom_fichier> le header est alors dans /usr/include/ ou # include "nom_fichier" le header est alors dans le répertoire courant

3 Définition d'une fonction: La définition d'une fonction décrit le type de la valeur retournée par la fonction, la liste des arguments et leur type qui seront passés par le module appelant au moment de l'appel, et le bloc d'instructions qui compose la fonction en question. Cette définition s'effectue de la sorte:

<type> <identificateur_fonction> (type paramètres) { ------------; ------------; return expression ou variable ou valeur; }

Page 47: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation procédurale: du C-ANSI au C++ 46

La dernière instruction est toujours un return suivi d'une expression (sauf dans le cas où la fonction ne retourne pas de valeur à l'appelant). Une fonction peut ainsi renvoyée grâce à l'instruction return une valeur scalaire (char, int, float), un pointeur ou une structure. Exemple : int max(int a, int b) { if (a>b) return (a); else return (b); }

int max(int a, int b) { return ((a>b) ? a : b);

}

Remarques : 1. lorsque le type de retour d'une fonction n’est pas précisé, il s'agit par défaut du type int; 2. les arguments dans la définition d'une fonction peuvent être de type char, int, float,

pointeurs ou structure; 3. une fonction n'a pas forcément besoin d'arguments. Dans ce cas son identificateur est suivi

de deux parenthèses avec le qualificatif void (vide), qui signifie qu'il n'y a pas d'arguments (char menu(void) par exemple)

4.une fonction ne renvoie pas forcément une valeur à l'appelant. On le précise par l'emploi de void (vide) qui signifie que la fonction ne renvoie rien. Une fonction qui ne renvoie rien est également appelée procédure (void menu(int a)par exemple)

5. en langage C, il est interdit de définir une fonction à l'intérieur de la définition d'une fonction. Un programme C est donc constitué d'une suite de fonctions qui ne peuvent être emboîtées. Le main est une fonction particulière qui constitue le point d'entrée du programme.

4 Appel d'une fonction et passage de paramètres: Lorsqu'une fonction a été déclarée et définie, elle peut être utilisée à tout moment en écrivant son nom suivi des arguments d'appel qui doivent être passés dans l'ordre prévu. C'est donc au moment de l'appel, qu'on indique sur quels objets on veut que la fonction travaille effectivement, c'est pourquoi on les désigne par le terme de paramètres effectifs. En revanche, lors de la définition d'une fonction, on précise le type retourné par la fonction (void si c'est une procédure) ainsi que le nombre et le type des paramètres qui seront ultérieurement passés à cette fonction. On parle alors dans la définition de paramètres formels. En langage C le seul mécanisme de passage des paramètres existant est le passage par valeur, c'est à dire qu'il y a recopie dans la pile au moment de l'appel de la fonction, des paramètres effectifs passés. La fonction va donc travailler sur une image des paramètres, et s'il y a des modifications, elles ne concernent que cette image. Ainsi lorsque la fonction est terminée du point de vue de l'exécution, les paramètres effectifs n'auront pas été modifiés.

Page 48: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 47

Si l'on veut garder des modifications, il faut passer comme paramètres effectifs non plus les variables, mais les adresses de ces variables. Il y aura là aussi recopie, mais cette recopie va concerner les adresses. Comme on indique à l'intérieur de la définition de la fonction qu'il faut travailler sur ce qui est pointé par ces adresses on va donc modifier non plus une image des variables mais les variables elles-mêmes. Une autre solution consisterait à définir la variable passée par adresse comme paramètre à une fonction, en tant que variable globale ce qui étend alors son domaine de visibilité à tout le programme. Mais dans ce cas tout autre fonction peut la modifier, et d'autre part la fonction n'est plus alors paramètrée. Il vaut donc mieux éviter cette approche contraire à certains principes de programmation (voir chapitre 6). Exemple: Considérons une procédure qui permute deux entiers. Le passage doit être effectué par adresse, si on veut que les modifications soit effectives dans la fonction appelante. void permute(int *a, int*b) { int buffer=*a; *a=*b; *b=buffer; } Si on considère que la fonction appelante veut voir exécuter la fonction permute sur deux entiers x et y, l'appel de permute se fait ainsi :

permute(&x,&y); 5 Fonctions et variables: 5-1 Domaine de validité des objets: Nous avons déjà vu qu'un objet (au sens procédural du terme) est décrit par 3 informations:

• un identificateur = son nom • une valeur constante ou variable • un type prédéfini ou construit

Les constantes sont définies en tête du fichier source par l'instruction #define destinée au préprocesseur qui effectue les substitutions nécessaires dans tout le fichier. Les variables peuvent être définies en divers endroits du fichier source ce qui détermine leur visibilité ou espace de validité.

Page 49: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 48

- variable déclarée en tête du fichier source: ceci correspond à une variable globale. Cette variable a une durée de vie égale à celle du programme et est disponible donc modifiable pour tous les modules, lorsque le programme est constitué à partir de plusieurs modules (ou fichiers) source. - variable déclarée à l'intérieur d'une fonction: c'est une variable locale ou interne à la fonction dans laquelle elle a été définie. Néanmoins, une variable locale peut être modifiée par une autre fonction si celle ci est passée par adresse. La durée de vie d'une variable locale est temporaire et limitée par défaut à la durée d'exécution du bloc où elle est définie. Remarque: La redéfinition à un niveau local d'une variable globale, masque la définition du niveau global. L'objet ainsi défini au niveau interne est un autre objet indépendant de celui qui a été défini au niveau global. 5-2 Classes de mémorisation d'une variable: Le paragraphe précédent a permis de préciser que:

• une variable globale a une durée de vie permanente égale au temps d'exécution de tout le programme.

• une variable locale a une durée de vie temporaire liée à la durée d'exécution de la fonction dans laquelle elle est définie. La place mémoire nécessaire est allouée au moment de l'exécution de la fonction par le système et est restituée à la fin de l'exécution de la dite fonction.

Cependant en langage C, le programmeur peut déterminer la classe de mémorisation d'une variable: - dans le cas d'une variable globale: si elle est déclarée static, cette variable n'est vue que par le fichier source où elle est définie. Dans le cas contraire, elle peut être vue par d'autres fichiers (ou modules) source qui la référencent par la déclaration extern. - dans le cas d'une variable locale: si on déclare une variable locale static, son domaine de visibilité reste limité à la fonction dans laquelle elle est définie, mais sa durée de vie devient permanente. C'est une donnée rémanente, c'est à dire qu'elle est conservée même quand l'exécution de la fonction est terminée. Par défaut, une variable locale a une classe de mémorisation de type auto, c'est à dire à durée de vie limitée. Il existe également un troisième type pour les variables locales dit register qui est identique au type auto, sauf que le programmeur demande une allocation dans les registres du processeur. Les variables à durée de vie permanente (variables globales et les variables locales de classe static) ont une place mémoire allouée pour toute l'exécution du programme. Leur valeur est rémanente.

Page 50: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 49

Les variables à durée de vie temporaire (variables locales de classe auto ou register) sont créées au moment de l'exécution de la fonction où elles sont définies et leur place mémoire est libérée à la fin du module. Ce mécanisme d'allocation mémoire s'appuie sur la gestion d'une pile.

5-3 Initialisation des variables: Pour les variables globales, l'initialisation se fait une fois pour toute à la compilation lors de la définition. En l’absence d'initialisation explicite, ces variables sont mises à 0. Pour les variables locales de classe auto ou register, l'initialisation se fait à chaque appel de la fonction où elles sont définies. En l’absence d'initialisation, elles peuvent prendre n'importe quelle valeur parasite présente au moment de l'allocation mémoire. Pour les variables locales static, l'initialisation a lieu à la compilation comme pour les variables globales. 5-4 Objets atteints par une fonction: L'environnement d'une fonction regroupe trois notions: les entrées: il s'agit des variables et constantes que la fonction peut atteindre en lecture la sortie: il s'agit du résultat produit par la fonction les entrées-sorties: il s'agit des variables que la fonction peut atteindre et modifier Les entrées d’une fonction sont constituées:

• des variables globales • des paramètres passées à la fonction soit par valeur soit par adresse

La sortie d’une fonction est la valeur retournée par la fonction Les entrées-sorties d’une fonction sont constituées:

• des variables globales • des paramètres passés par adresse

valeur retournée variables globales variables globales paramètres Fonction paramètres passés par adresse

Page 51: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 50

Remarque: Une variable créée dynamiquement à l’intérieur d’une fonction par un malloc(), reste visible à l’extérieur de la fonction (puisque créée dans le tas et non la pile), pourvu qu’on ait son adresse. Le fait de quitter une fonction ne libère donc pas automatiquement l’espace mémoire alloué dynamiquement à l'intérieur de cette fonction. La libération reste donc à la charge du programmeur par l'intermédiaire de la fonction free(). 6 Récursivité: Une fonction récursive est une fonction qui est définie en référence à elle-même. Ceci implique dans la définition la nécessité absolue de prévoir une condition de terminaison pour éviter un nombre infini d'appels. La récursivité permet de traduire la notion mathématique de récurrence dont le principe général est de ramener la résolution d'un cas à celle du cas précédent. Elle s'applique naturellement aux structures de données dont la définition est elle-même récursive comme les listes ou les arbres par exemple. Exemple de fonction récursive: La fonction factorielle se définit par la récurrence n!=n*(n-1) avec 0!=1. La définition de cette fonction s'écrit alors:

int facto(int x) { if (x==0) /* condition d'arret */ return 1; return x*facto(x-1); }

Exemple de procédure récursive: Considérons une liste simplement chaînée dont chaque cellule comprend un entier et un pointeur sur la cellule suivante (NULL si fin de liste). La définition d'un telle liste est alors:

typedef struct cell { int corps; struct cell * suivant; } * LISTE;

LISTE debut;

Page 52: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 51

Supposons la liste créée. On veut afficher le contenu de cette liste mais dans l'ordre inverse. La fonction qui effectue ce traitement s'écrit récursivement:

void aff_liste(LISTE l) { if (l!=NULL) { /* condition d'arret */ aff_liste(l->suivant); /* appel recursif */ printf(" %d ",l->corps); } }

La récursivité fonctionne parce qu'il y a mémorisation de l'état de l'appel précédent et que cet état est retrouvé au retour de chaque appel. Le mécanisme utilisé est celui d'une pile dans laquelle on sauvegarde les arguments passés lors de l'appel, les variables locales si il y en a et l'adresse de retour. Lorsque la condition d'arrêt est réalisée, on peut dépiler et effectuer les traitements associés à chacun des retours. Ce principe est illustré avec l'exemple suivant à partir de la fonction facto définie précédemment: facto(3) x=3 3*facto(2) x=2 2*facto(1) x=1 1*facto(0) x=0 return 1 2*1 return 2 3*2 return 6 6 Remarque: Lorsqu'on veut initialiser une variable locale d'une fonction récursive, celle-ci ne doit l'être bien entendu qu'une seule fois au moment du premier appel récursif. On utilise alors la classe de mémorisation static. Mais attention à l'effet de bord possible si la fonction modifie cette variable locale. En effet celle-ci est alors modifiée de façon rémanente de sorte que si la fonction est à nouveau invoquée, la variable en question n'est plus initialisée à la valeur prévue initialement. Une solution élégante consiste à écrire une fonction d'appel qui invoque la fonction récursive en l'initialisant correctement chaque fois.

Page 53: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 52

CHAPITRE 4 - LES FICHIERS 1 Présentation: Du point de vue logique un fichier est une collection de données sur mémoire de masse (disque, disquette,....). On distingue généralement les fichiers textes et les fichiers binaires. Un fichier texte est composé d'une collection de lignes, une ligne étant une suite de caractères codés en ASCII terminée par le marqueur '\n'; lorsque les lignes sont une collection d'objets identiques de type structure, on parle aussi de fichier d'enregistrements. Un fichier binaire contient une suite d'octets, chaque octet pouvant prendre une valeur entre 0x00 et 0xFF. Du point de vue physique, c'est à dire pour le système Unix un fichier est un ensemble de blocs contenant une succession d'octets sans structure spéciale. Chaque fichier est caractérisé par un i-node qui contient les enregistrements nécessaires à la gestion du fichier, en particulier:

• les droits d'accès • le propriétaire • la taille en octets • le nombre de liens sur le fichier • la date de la dernière modification • l'adresse physique sue le disque

La définition des structures nécessaires à la description de l'i-node d'un fichier se trouve dans les headers ino.h et inode.h situés dans le répertoire /usr/include/sys. Les types d'opération effectuées sur un fichier sont: - l'ouverture et la fermeture du fichier

- la lecture ou l'écriture d'un ou plusieurs octets du fichier En langage C, il existe plusieurs fonctions à la disposition du programmeur pour gérer les entrées-sorties sur les fichiers. On distingue les fonctions dites de bas niveau qui utilisent en fait le niveau d'entrée-sortie du système d'exploitation, des fonctions dites de niveau supérieur qui sont censées offrir plus de portabilité, en masquant les spécificités des appels systèmes de bas niveau. Les fonctions de haut niveau font partie de la bibliothèque standard et s'appuient sur la gestion de tampons (ou buffers cf. paragraphe 4) appartenant à l'espace d'adressage des processus effectuant des entrées-sorties. Pour les fichiers en général, il faut distinguer l'organisation et la méthode d'accès. Le système Unix ne supporte de façon native que des fichiers d'organisation séquentielle, sur lesquels on ne peut faire que de l'accès séquentiel par caractère, par ligne ou par format. Il est possible cependant d'effectuer un accès sélectif sur des fichiers séquentiels en se déplaçant d'un nombre n d'octets dans le fichier, en avant ou en arrière. Ainsi, si tous les enregistrements du fichier ont la même taille, il est alors possible d'accéder directement à un des enregistrements.

Page 54: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 53

Mais pour implanter des fichiers d'organisation séquentielle indexée, il faut passer par des bibliothèques spécifiques de fonctions qui permettent de gérer ce type d'organisation de fichiers, et les méthodes d'accès associées. 2 Les fonctions de niveau supérieur: L'ensemble des problèmes de communication entre le programme et les organes périphériques sont pris en charge par le système d'exploitation (cf. le cours système). Au niveau supérieur des entrées sorties, le programmeur n'a pas à connaître les détails des modalités de ces opérations d'entrée sortie, mais il doit disposer de moyens lui permettant de désigner un fichier physique et de savoir s'il peut travailler avec. A ce niveau, on utilise un descripteur de fichier appelé File Pointer qui est un pointeur sur une structure baptisée FILE prédéfinie dans stdio.h. Il existe 3 File Pointer réservés qui sont:

- stdin : fichier d'entrée standard - stdout: fichier de sortie standard - stderr: le fichier des erreurs 2-1 Ouverture et fermeture de fichiers:

FILE *fopen(char *name, char* mode); Cette fonction ouvre le fichier précisé par la chaîne de caractères name. Le nom donné est ici le nom physique du fichier sur disque. Le paramètre mode qui est aussi une chaîne de caractères précise si on ouvre le fichier

- en lecture mode = "r" (read) - en écriture mode = "w" (write)

- en extension mode = "a" (append) - mise à jour mode = "r+" - mise à jour avec création d'un nouveau fichier mode = "w+"

- sous certains systèmes il faut rajouter "+b" pour les fichiers binaires ce qui est inutile sous Unix

La fonction fopen renvoie un File Pointer associé au fichier précisé. S'il y a eu une erreur à l'ouverture, cette fonction renvoie le pointeur NULL (NULL est une constante symbolique définie dans <stdio.h>). Si on ouvre en mode "w" un fichier qui n'existe pas, il est alors créé; en revanche s'il existait déjà, l'ancien est écrasé. Si on ouvre en lecture un fichier inexistant, la fonction fopen renvoie NULL. Il y a autant de File Pointer que de fichiers ouverts dans le programme mais le nombre de fichiers qu'il est possible d'ouvrir en même temps est limité par le système. Les trois fichiers standards sont ouverts en permanence.

Page 55: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 54

int fclose(FILE *fp); La fonction fclose réalise l'opération inverse de fopen. Elle libère le File Pointer fp qui avait été alloué au fichier, le rendant ainsi disponible pour un autre fichier éventuellement. Cette fonction provoque également l'écriture du tampon sur le fichier si il contient des informations qui n'ont pas été encore écrites. Elle retourne -1 en cas d'erreur, 0 sinon. 2-2 Ecriture dans le fichier: Il existe plusieurs possibilités: - écriture d'un caractère : putc - écriture d'une chaîne de caractères : fputs fichier texte - écriture formatée : fprintf - écriture de n octets : fwrite fichier binaire

int putc(char c, FILE *fp) ; Cette fonction met le caractère c dans le fichier désigné par le File Pointer fp, et retourne un entier (et non un caractère) qui permet de savoir si cela c'est bien passé. Si cela s'est mal passé putc retourne EOF qui est un entier à la valeur -1 (EOF signifie fin de fichier "End Of File"; il s'agit d'une constante symbolique définie dans <stdio.h>). Remarques: 1. putc est une variante macro-instruction de fputc qui est une fonction 2. putchar est un cas particulier de putc ou le File Pointer est stdout:

putchar(c) est identique à putc(c,stdout)

int fputs(char *pt; FILE *fp); Cette fonction écrit dans le fichier désigné par fp, la chaîne de caractères pointée par pt. Cette chaîne doit bien sûr se terminer par '\0'. La fonction retourne EOF en cas d'erreur et 0 sinon. Remarques: 1. fputs écrit dans le fichier fp la chaîne pt jusqu'au caractère '\0' non compris et sans

ajout de caractère '\n' 2. puts, variante de fputs, agit sur stdout; le caractère '\0' est remplacé par '\n'

Page 56: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 55

int fprintf(FILE *fp, char *format, arg1..argn) ; Cette fonction écrit dans le fichier fp, l'entrée ou les entrées figurant dans la liste d'arguments arg et décodées suivant le ou les formats précisés par format. Elle est identique à printf, notamment en ce qui concerne les formats, sauf que la sortie n'est plus stdout mais fp. La fonction fprintf retourne le nombre de caractères écrits sur la sortie ou bien EOF en cas d'erreur.

int fwrite(char *buf, unsigned t, unsigned n, FILE *fp) ; Cette fonction écrit n éléments de taille t dans le fichier fp à partir de la zone mémoire pointée par buf. Elle renvoie le nombre d'éléments écrits qui est inférieur à n en cas d'erreur. 2-3 Lecture dans un fichier: Pour la lecture dans un fichier il existe aussi plusieurs possibilités: - lecture d'un caractère : getc - lecture d'une chaîne de caractères : fgets fichier texte - lecture formatée : fscanf - lecture de n octets : fread fichier binaire

int getc(FILE *fp); Lit un caractère dans le fichier désigné par le File Pointer fp, et retourne un entier qui est soit le caractère lu (un caractère peut être considéré comme un entier) soit l'entier EOF, ce qui permet de savoir si on a atteint la fin du fichier lu. Remarques: 1. getc est une variante macro-instruction de fgetc qui est une fonction. 2. getchar est un cas particulier de getc ou le File Pointer est stdin

getchar() ≡ getc(stdin) 3. getc renvoie un entier. S'il vaut -1, cette valeur est distincte de toutes les valeurs

possibles des caractères. Il faut donc bien faire attention d'affecter la valeur retournée par getc dans un entier car si c'est dans un caractère, la comparaison avec la constante prédéfinie EOF (entier à -1) risque d'être vraie avec un caractère à FF (ce qui ne correspond pas à la fin du fichier), ou bien jamais vrai si le fichier contient des caractères codés sur 7 bits (unsigned char).

Page 57: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 56

char *fgets(char *pt , int n, FILE *fp) ; Cette fonction lit sur le fichier désigné par fp, une chaîne de caractères jusqu'à un caractère d'interligne '\n' et pour un maximum de n-1 caractères ou jusqu'à la fin de fichier. La chaîne est affectée à la zone mémoire pointée par pt. La fonction retourne la chaîne ou NULL en cas de fin de fichier ou encore en cas d'erreur. Remarques: 1. fgets renvoie un pointeur sur caractère et non un entier. 2. si un caractère '\n' est lu il est conservé. 3. un caractère '\0' est systématiquement rajouté en fin de chaîne. 4. gets est une variante de fgets et agit sur stdin. Au contraire de fgets, le caractère '\n' de fin de ligne est supprimé.

int fscanf(FILE *fp, char *format ,....) ; Cette fonction effectue une lecture formatée sur le fichier désigné par le File Pointer fp, et suivant le format précisé par format (idem à la fonction scanf, donc nombre de paramètres variables). Le ou les champs lus sont affecté au paramètre correspondant dans la liste. Comme scanf, cette fonction renvoie le nombre de champs décodés et affectés ou bien EOF. Exemple: On veut lire un champ dans le fichier désigné par fp sous forme de chaîne de caractères et le récupérer dans une zone mémoire pointée par pt (pt étant de type char *). Il faut donc écrire:

fscanf(fp,"%s",pt); Remarque: Un champ de l'entrée peut se terminer:

• sur la largeur maximale précisée dans le format (ex "%10s") • lorsqu'un caractère d'espacement est lu. Les caractères d'espacement sont le blanc,

l'interligne, le retour charriot et la tabulation. • lorsqu'un caractère lu sur l'entrée ne peut être décodé

int fread(char *buf, unsigned t, unsigned n, FILE *fp) ; Cette fonction lit n éléments de taille t dans le fichier fp, et les écrit dans la zone mémoire pointée par buf. Elle renvoie le nombre d'éléments lus et 0 en cas d'erreur ou de fin du fichier. L'avant dernière lecture peut donc renvoyer un nombre inférieur à n et la dernière renvoie 0.

Page 58: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 57

2-4 Fonctions pour l'accès sélectif: int fseek (FILE *fp, long offset, int origin) ; Cette fonction permet de déplacer le pointeur d'emplacement de offset octets depuis le point de départ indiqué par origin avec comme valeurs: 0 début du fichier; 1 position courante; 2 fin du fichier. La fonction retourne EOF en cas d'erreur, 0 sinon. Remarques: 1. offset doit être un entier long (ex 3L) 2. pour revenir en arrière, offset doit être négatif. 3.les valeurs du paramètre origin peuvent être remplacées par les constantes suivantes

définies dans <stdio.h > SEEK_SET 0 SEEK_CUR 1 SEEK_END 2

long ftell(FILE *fp) ; Cette fonction renvoie la valeur courante du pointeur d'emplacement et retourne -1L en cas d'erreur. void rewind(FILE *fp) ; Cette procédure positionne le pointeur d'emplacement au début du fichier fp. 2-5 Autres fonctions sur les fichiers: Les fonctions les plus couramment employées sur les fichiers sont les suivantes:

FILE *freopen(char *name, char *mode, FILE *fp) ; Cette fonction ferme le fichier désigné par fp et associe fp au nouveau fichier de nom name. Le champ mode joue le même rôle que dans la fonction open.

int fflush(FILE *fp) ; Sur un flot de sortie, cette fonction force l'écriture du tampon dans le fichier sans le fermer. Renvoie EOF en cas d'erreur, 0 sinon.

Page 59: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 58

void setbuf(FILE *fp, char *buf) ; Création d'une bufferisation; le buffer associé à fp est désigné par buf; stdin et sdtout sont automatiquement bufferisés.

int feof(FILE *fp); Cette fonction renvoie une valeur non NULL (donc vraie) si la fin de fichier sur fp est détectée. Elle permet faire la distinction (après un retour de EOF d'une autre fonction) entre l'erreur et la fin de fichier.

int ferror(FILE *fp); Cette fonction renvoie une valeur non NULL après une erreur sur le fichier désigné par fp. L'erreur reste positionnée jusqu'à la fermeture du fichier ou un appel à la fonction clearerr.

void clearerr(FILE *fp) ; Efface les indicateurs de fin de fichier ou d'erreur pour fp. 3 Les fonctions de bas niveau: Il s'agit de fonctions correspondant à des appels système de gestion des entrées-sorties non bufferisées. Avec ces primitives, les fichiers sont référencés par des "File Descriptor" qui sont de type entier. Il existe trois File Descriptor prédéfinis et constamment ouverts: 0 : fichier d'entrée standard 1 : fichier de sortie standard 2 : fichier des erreurs

int creat (char *name, int protect) ; Si le fichier de nom name existe déjà, cette fonction l'ouvre en écriture (donc écrase tout ce qu'il y avait). Sinon elle le crée avec les droits d'accès précisés par l'argument protect. Ces droits d'accès sont exprimés comme dans la commande chmod du système Unix. Si la création s'est bien passée, la fonction retourne un entier qui sert de File Descriptor pour le fichier, sinon elle renvoie un entier négatif.

Page 60: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 59

int open(char *name, int access, int perms) ; Cette fonction ouvre le fichier de nom name dans le mode exprimé par access avec les autorisations perms. Elle renvoie le File Descriptor associé au fichier ou -1 en cas d'erreur. L'argument access prend les valeurs 0 en lecture, 1 en écriture, 2 en mise à jour. Cependant, il est possible d'utiliser des constantes symboliques suivant le système (par exemple O_RDONLY, O_WRONLY, O_RDWR, O_TRUNC, O_APPEND et O_CREAT définis dans fcntl.h et qui peuvent être combinées entre elles avec le OU logique |). L'argument perms qui est optionnel, prend les valeurs classiques des droits d'accès.

int close(int fd) ; Ferme le fichier référencé par fd.

int unlink(int fd ) ; Détruit le fichier référencé par fd.

int read(int fd, char *buf, int nbr ); Lit nbr octets dans le fichier référencé par fd, et les place dans la zone mémoire désignée par buf. Renvoie le nombre exact d'octets réellement lus, 0 en cas de fin de fichier, et -1 en cas d'erreur.

int write(int fd, char *buf, int nbr ); Cette fonction écrit nbr octets depuis la zone mémoire pointée par buf dans le fichier référencé par fd et à partir de la position courante. Cette fonction retourne le nombre d'octets qui ont pu être réellement écrits et -1 en cas d'erreur.

long lseek(int fd, long offset, int origin) ; Cette fonction permet de modifier la position courante du fichier référencé par le File Descriptor fd, de offset octets à partir de la position indiquée par origin et dont les valeurs peuvent être: 0 début de fichier, 1 position courante, 2 fin du fichier. La fonction renvoie la nouvelle position dans le fichier ou -1 en cas d'erreur. Remarques: 1. offset doit être un entier long (ex 3L) 2. pour revenir en arrière, offset doit être négatif.

Page 61: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 60

4 Différences entre les E/S bufferisées et les E/S non bufferisées: Les entrée-sorties de haut niveau sont bufferisées. Le terme buffer est un anglicisme que l'on peut traduire par tampon et qui désigne en fait un espace mémoire permettant au système de gestion de fichier d’effectuer des transferts optimisés par bloc physique. Ceci permet de minimiser les opérations d’entrées-sorties physique (car le tampon de caractères est dans l'espace mémoire du programme) et a priori d'améliorer les performances. Cette gestion de tampon intermédiaire peut se traduire par une représentation non exacte de l'état du programme pour les écritures. En effet, les écritures sont différées et le tampon d'écriture n'est vidé que lorsqu'une fin de ligne est transmise, que le tampon est plein ou encore qu'une fonction force le vidage (fflush(), fclose(), fseek()). Les exemples suivants permettent de mettre en évidence ce problème. Premier programme : /*************************************************************************/ /* test1.c : Exemple de comportement de l'ecriture du tampon sur le */ /* fichier avec des E/S bufferisees */ /*************************************************************************/ #include <stdio.h> char buff[7]; FILE *fp; main() { fp=fopen("ESSAI","w+"); fwrite("essai1",6,1,fp); printf("\n contenu du fichier apres ecriture de essai1 : "); system("cat ESSAI"); kbget(); /* kbget = fonction personnelle de saisie au vol */ fseek(fp,-6L,1); printf("\n pointeur d'emplacement apres le fseek: %d",ftell(fp)); printf("\n contenu du fichier apres le fseek : "); system("cat ESSAI"); kbget(); fwrite("essai2",6,1,fp); printf("\n pointeur d'emplacement apres le fwrite: %d ",ftell(fp)); printf("\n contenu du fichier apres essai2 : "); system("cat ESSAI"); kbget(); fflush(fp); printf("\n contenu du fichier apres le fflush : "); system("cat ESSAI"); kbget(); fwrite("essai3",6,1,fp); printf("\n contenu avant le fclose avant essai3 : "); system("cat ESSAI"); kbget(); fclose(fp); printf("\n contenu apres le fclose : "); system("cat ESSAI"); kbget(); }

Page 62: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 61

Résultat de l'exécution : -> test.e contenu du fichier apres ecriture de essai1 : pointeur d'emplacement apres le fseek: 0 contenu du fichier apres le fseek : essai1 pointeur d'emplacement apres le fwrite: 6 contenu du fichier apres essai2 : essai1 contenu du fichier apres le fflush : essai2 contenu avant le fclose avant essai3 : essai2 contenu apres le fclose : essai2essai3 Ce premier programme permet de constater que:

• l'appel à la fonction fwrite n'implique forcément une écriture physique sur le disque sauf si le tampon est plein

• l'appel à la fonction fseek, flush ou fclose entraîne un transfert sur le disque s'il y a des données en attente d'écriture dans le tampon

Deuxième programme : Supposons que l'on veuille maintenant travailler sur un fichier en mise à jour. L'exemple suivant montre comment procéder avec des fonctions de bas niveau non bufferisées. /************************************************************************/ /* test2.c : Exemple de mise a jour d'un fichier avec des fonctions de */ /* bas niveau */ /************************************************************************/ #include <stdio.h> #include <fcntl.h> /* Definitions des constantes O_RDWR soit 0000002 et SEEK_CUR soit 1 */ char buff[10]; int fd; main() { fd=open("ESSAI",O_RDWR); /* ouverture en MAJ */ read(fd,buff,6); printf("enreg lu : %s\n",buff); /* lecture */ write(fd,"essai1",6); /* ecriture */ lseek(fd,-6,1); /* positionnement */ read(fd,buff,6); /* lecture */ printf("enreg lu : %s\n",buff); close(fd); }

Page 63: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 62

1) contenu du fichier avant le programme: -> more ESSAI ------suitedelaligne 2) résultat de l'exécution du programme: -> test2.e enreg lu : 123456 enreg lu : essai1 3) contenu du fichier après le programme: -> more ESSAI 123456essai1suitedelaligne Pour obtenir les mêmes résultats avec des fonctions de la bibliothèque standard, il faut penser à vider le tampon entre les ordres de lecture et d'écriture. Le programme précédent est donc traduit de la sorte avec les fonctions de la bibliothèque standard: /************************************************************************/ /* test2.c : Exemple de mise a jour d'un fichier avec des fonctions de */ /* la bibliotheque standard (E/S bufferisees ) */ /* Il faut donc vider le buffer a chaque fois qu'on passe de */ /* la lecture a l'ecriture et vice et versa */ /*************************************************************************/ #include <stdio.h> char buff[10]; FILE *fp; main() { fp=fopen("ESSAI","r+"); fread(buff,6,1,fp); printf("enreg lu : %s\n",buff); /* Il faut vider le buffer physique avant le fwrite */ fflush(fp); fwrite("essai1",6,1,fp); /* ou fscanf(fp,"%s","essai1") */ /* Il faut vider le buffer physique avant le fseek */ fflush(fp); fseek(fp,-6L,1); fread(buff,6,1,fp); printf("enreg lu : %s\n",buff); fclose(fp); }

Page 64: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 63

Page 65: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 64

CHAPITRE 5 - COMPLEMENTS 1 Passage d'arguments à un programme: Il est possible de passer des paramètres à un programme. Comme le point d'entrée du programme est la fonction main, c'est à elle qu'il faut passer les paramètres. Pour cela on utilise principalement deux arguments qui sont argc et argv: • argc est un entier qui indique le nombre d'argument. • argv est un tableau contenant les chaînes de caractères constituant les paramètres passés

au programme au moment de l'appel. Il existe cependant la possibilité de récupérer un troisième argument appelé généralement arge de type char **, qui est un tableau de chaînes terminé par le pointeur NULL. Chaque chaîne contient la valeur des variables d’environnement; sous Unix par exemple, cela permet de récupérer les valeurs des variables systèmes comme $USER, $HOME, $PATH, etc. La valeur de ces chaînes de caractères ainsi que leur nombre dépend de l’environnement de l'utilisateur du système. Ces arguments sont déclarés de la sorte: main(int argc, char **argv, char **arge) Exemple: soit un programme auquel on passe deux paramètres 1 et tp.c impr 1 tp.c argc vaut 3 car le nom du programme est compté argv[0] est la chaîne "impr" c'est à dire le nom du programme argv[1] est la chaîne "1" argv[2] est la chaîne "tp.c" Le véritable premier paramètre passé au programme est bien sûr en fait argv[1];

Page 66: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 65

2 Les déclarateurs complexes: 2-1 Généralités: Il est possible de définir des éléments complexes comme par exemple des tableaux de pointeurs, des pointeurs sur fonctions, des tableaux de pointeurs sur des fonctions, des fonctions renvoyant des pointeurs sur fonctions etc. Quelques exemples: char *ptr[5]; ptr est un tableau de 5 pointeurs sur caractères; char *ptr(); ptr est une fonction qui renvoie un pointeur sur caractères char (*ptr)(); ptr est un pointeur sur une fonction qui renvoie un caractère char *(*ptr)(); ptrest un pointeur sur une fonction qui renvoie un pointeur sur

un caractère char *(*ptr())(); ptr est une fonction qui renvoie un pointeur sur une fonction qui

renvoie un pointeur sur caractère char (*(*ptr)())(); ptr est un pointeur sur une fonction qui renvoie un pointeur

sur une fonction qui renvoie un caractère

char *(*(*ptr)())(); ptr est un pointeur sur une fonction qui renvoie un pointeur

sur une fonction qui renvoie un pointeur sur caractère char (*ptr[10])(); ptr est un tableau de 10 pointeurs sur des fonctions qui

renvoient un caractère char *(*ptr[10])(); ptr est un tableau de 10 pointeurs sur des fonctions qui

renvoient un pointeur sur caractères Remarque : On ne peut pas faire un tableau de fonctions ou encore une fonction retournant un tableau. Il faut passer par des pointeurs.

Page 67: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 66

2-2 Cas des pointeurs sur fonctions: En langage C, il est possible de définir des pointeurs sur fonctions. Un pointeur sur une fonction peut alors être passé comme argument dans l'appel d'une fonction, être retourné par une fonction, faire partie d'un tableau de pointeurs sur fonctions.

a) Déclaration d’un pointeur sur fonction : Pour déclarer un pointeur sur fonction, on procède de la sorte :

type_retourne (* pf) (type_des_arguments_attendus) ; Attention : Ne pas confondre

type_retourne (* pf) ()

qui est la déclaration d'un pointeur pf sur une fonction qui renvoie une variable de type type_retourne avec

type_retourne *pf ()

où pf est une fonction qui renvoie un pointeur sur type_retourne.

b) Déclaration d’un tableau de pointeurs sur fonction :

Pour déclarer un tableau de pointeurs sur fonctions, on écrit: type_retourne (*nom_tab[TAILLE]) (type_arguments_attendus) ; Le tableau doit contenir des pointeurs qui pointent sur des fonctions qui retournent le type précisé.

c) Appel d’une fonction par un pointeur : Pour appeler la fonction pointée par le pointeur, il suffit d'écrire :

(*pf) (liste_d'arguments_effectifs) ;

*pf désigne la cible du pointeur, les parenthèses signifiant qu'il s'agit d'une fonction, comme dans le cas d'un appel classique à une fonction.

Page 68: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 67

Dans le cas d'un appel par un tableau de pointeur, il faut en plus préciser quel le pointeur choisi dans le tableau, par exemple (*tab_ptf[i]) (arguments_effectifs);

d) Initialisation d’un pointeur sur fonction : D'après ce qui précède, le nom d'une fonction correspond en fait à l'adresse de cette fonction. En effet, appeler la fonction fonc() est équivalent à écrire (*ptf)() si ptf est un pointeur pointant sur cette fonction. On peut donc initialiser le pointeur sur une fonction en écrivant l'égalité

ptf=fonc;

De même, un tableau de pointeurs sur fonction sera initialisé par la liste des noms des fonctions sur lesquelles on veut que chaque élément du tableau pointe. Exemple :

void proc1(void), proc2(void), proc3(void); /******************************************************************** définition du type TABFONC tableau de 10 pointeurs sur des procédures et déclaration d'une variable tabfonc de type TABFONC On initialise la tableau avec les identificateurs proc1, proc2 et proc3 qui représentent en fait les adresses de ces procedures ********************************************************************/ typedef void (*TABFONC[10])(void); TABFONC tabfonc={proc1,proc2,proc3};

Pour appeler par exemple la procédure proc1 depuis tabfonc, il suffit alors d'écrire :

(* tabfonc[0]) () ; /* appel de proc1() */

e) cas d'utilisation des pointeurs sur fonction : Les pointeurs sur fonctions sont utilisés classiquement lorsque:

- on veut passer une fonction en tant que paramètre d'une autre fonction; un exemple est donné au chapitre 6 paragraphe 5 avec la réalisation du module tableau

- on veut définir une fonction qui doit retourner une fonction; -

Exemple :

void fonc1(int x, char (* ptf)(int,int)); /* fonc1 reçoit comme paramètres un entier et un pointeur sur fonction qui reçoit 2 entiers et qui retourne un char */ void (* f1(void))(int); /* f1 est une fonction qui ne reçoit aucun argument et qui retourne un pointeur sur une procédure recevant comme argument un entier */

Page 69: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 68

3 Fonction à nombre variable d'arguments: 3-1 Définition: La définition d'une fonction à nombre variable d'arguments nécessite : − l'inclusion du fichier d'entête <stdarg.h> ou <varargs.h> − un pointeur de type prédéfini va_list − trois macros va_start, va_arg et va_end

Le pointeur de type va_list permet de récupérer les arguments non nommés. Il est positionné grâce à la macro va_start et peut être déplacé par la macro va_arg. La macro va_start(ptr, nom_arg) positionne le pointeur ptr sur le premier argument non nommé nom_arg. Le pointeur ptr est de type va_list et le paramètre nom_arg est le nom du dernier argument obligatoire. La macro va_arg(ptr,nb)déplace ptr dans la pile des arguments d'un nombre d'octets correspondant à nb (type ou sizeof) et renvoie l'argument pointé par ptr. La macro va_end(ptr) restaure un état cohérent de la pile des arguments et reçoit comme argument le pointeur ptr qui a permis de manipuler la pile.

Une fonction à nombre variable de paramètres doit posséder une liste des arguments qui se terminer par ... et il doit y avoir au minimum un argument nommé. 3-2 Principe général pour parcourir la pile des arguments: Il faut d'abord déclarer un pointeur de type var_list puis l'initialiser par va_start. Le parcours de la pile se fait ensuite par appels successifs à va_arg. Il faut disposer d'une condition d'arrêt soit par un compteur qui indique le nombre d'arguments effectivement passés soit par un pointeur NULL pour terminer la liste d'arguments. A chaque appel à il faut indiquer le déplacement voulu en nombre d'octets. A la fin du parcours il faut restaurer la pile dans un état cohérent par la macro var_end. 3-3 Exemple: On considère la fonction fonc_exemple prototypée de la sorte

#include <stdio.h> #include <stdarg.h> void fonc_exemple(char * a1,int a2,...);

Page 70: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 69

Cette fonction prend deux arguments nommés a1 et a2 obligatoires, plus un nombre variable d'arguments matérialisé par les trois points ... Le corps de la fonction est le suivant:

void fonc_exemple(char * a1,int a2,...) /* il y a au moins un argument obligatoire */ { va_list ptr; /* pointeur sur liste d'arguments */ char *a3; double a4; va_start(ptr,a2); /* ptr pointe sur l'argument qui suit a2 */ if (a2 > 0) /* si il y a au moins un troisieme argument */ { a3 = va_arg(ptr,char *); /* ptr pointe sur l'argument 3 de type chaine */ printf("l'argument 3 est : %s\n",a3); }

if (a2==2) /* si il y a quatre arguments */ { a4 = va_arg(ptr,double); /* ptr pointe sur l'argument 4 de type double */ printf("l'argument 4 est : %f\n",a4); } /* restauration d'un etat correct de la pile */ va_end(ptr); } /* fin de la definition de la fonction */

Voici différents exemples d'appel de la fonction:

1) fonc_exemple("essai1", 0); 2) fonc_exemple("essai2", 1,"argument 3"); 3) fonc_exemple("essai3", 2,"encore un",3.14);

Résultats des appels:

1) ............................. 2) l'argument 3 est : argument 3 3) l'argument 3 est : encore un

l'argument 4 est : 3.140000

Page 71: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 70

CHAPITRE 6 - LES PROGRAMMES MULTI-FICHIERS 1 Introduction: La décomposition modulaire consiste à découper un programme en unités de programme plus simples (les modules) qui partagent entre elles des éléments (variables, constantes, fonctions ou procédures). Un module est donc un fichier source composé de fonctions. La décomposition s'impose souvent dans le monde professionnel compte tenu de l'importance des logiciels à développer et des avantages qu’apporte ce type de conception (réutilisation, fiabilité, maintenance, testabilité, optimisation des ressources lors de l'exécution) La conception modulaire repose sur un certain nombre de concepts : 1) l'abstraction: un module est une unité qui doit correspondre à une partie parfaitement

identifiée du problème 2) le couplage entre modules: il s’agit d’identifier les interférences entre les modules 3) l'encapsulation : on masque au maximum la complexité interne d'un module en ne

fournissant à l'extérieur uniquement ce qui est nécessaire à son utilisation. C’est la notion d’interface et de réalisation

4) les techniques de décomposition modulaire: soit par abstraction d'actions dans la conception orientée fonction, ou par abstraction d'objets dans la conception orientée objet

2 Couplage entre modules: 2-1 Mécanisme d’échange entre modules : Les échanges entre modules peuvent s'opérer en langage C, par le biais des variables globales publiques et par les demandes de service que constituent les appels de fonctions. Dans ce dernier cas, il n'est pas inutile de rappeler qu'une fonction peut: 1) en entrée consulter des variables globales (publiques ou privées) et les arguments 2) en sortie fournir un résultat soit par l'intermédiaire d'arguments qu'elle peut éventuellement

mettre à jour (en langage C via un pointeur), soit par les modifications qu'elle apporte à une ou plusieurs variables globales (publiques ou privées), soit enfin par la valeur qu'elle retourne (vide dans le cas d'une procédure)

Page 72: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 71

La conception modulaire nécessite donc du point de vue du langage C de bien maîtriser • les pointeurs, leur utilisation dans le mécanisme de passage d'arguments à une fonction, et

les pointeurs sur fonction (une fonction peut être également accessible par un pointeur) • la distinction entre la déclaration d'un objet (qui consiste à préciser son utilisation

publique ou privée) et sa définition (qui consiste, dans le cas d'une variable, à réserver la place mémoire nécessaire et éventuellement à l’initialiser, dans le cas d'une fonction, à préciser son type, ses paramètres et ses instructions)

• les règles de visibilité et de durée de vie d'une variable (locale, globale privée, globale publique) et d'une fonction (privée ou publique)

• la notion de compilation séparée (chaque module est une unité de compilation) et la notion de bibliothèque de fonctions (archivage de modules objets)

• les outils de production de logiciels 2-2 Déclaration et visibilité des variables et des fonctions: Ces notions ont déjà été abordées au chapitre 3. Dans le cas d'un même module une variable peut être globale ou locale avec les règles de visibilité qui en découlent. Dans le cas d'un programme multi-modules, une variable globale peut être soit globale seulement dans le module où elle est définie, soit globale à l'ensemble des modules qui constituent le programme et on parle alors de variable publique. Lorsqu'une variable globale définie dans un module est publique et donc accessible aux autres modules du programme, elle doit être déclarée en extern dans chacun des modules qui veulent utiliser cette variable. La classe extern est donc spécifiée dans les déclarations et non à la définition. Rappelons que la définition correspond à l'allocation mémoire nécessaire pour cette variable et à son initialisation. En revanche on ne peut pas initialiser une variable lors de sa déclaration en tant qu'extern. Lorsqu'on veut limiter la visibilité d'une variable globale au seul module où elle est définie, on utilise la classe static. Cette classe ne joue donc pas le même rôle dans le cas d'une variable locale que dans le cas d'une variable globale. Pour les fonctions, et par défaut, la définition se fait avec la classe extern. Cela signifie que par défaut, une fonction est visible dans tous les autres modules du programme. Lorsqu'on veut limiter la visibilité d'une fonction au seul module où elle est définie, il faut la définir avec la classe static.

Page 73: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 72

Exemple: corps de module.c /* définition d’une variable globale visible dans un autre module */ int nb_elem; /* définition d’une variable globale de visibilité limitée au module */ static int indice; /* déclaration d'une variable définie dans un autre module */ extern int valeur; : : /* définition d'une fonction visible dans les autres modules */ int fonc1() { ...... } /* définition d'une fonction de visibilité restreinte au module */ static int fonc2() { ..... } Remarque : La clause static doit être repérée au niveau du prototypage également. 2-3 Cas des variables globales: Lorsque toutes les fonctions d'un même module manipulent une même variable, il peut être avantageux de rendre cette variable globale. Cela ne pose aucun problème s'il s'agit d'une variable privée au module. En revanche, l'usage de variables globales publiques est plus délicat, car s'il permet, dans un échange entre deux modules, de limiter le nombre d'arguments lors d'un appel de fonction, il autorise également tout autre module non concerné par l'échange à accéder (et donc potentiellement modifier) les dites variables. En revanche, la technique d'appel par passage d'arguments permet de connaître exactement les objets manipulés et de savoir comment ces objets sont manipulés. On augmente ainsi la fiabilité du programme, mais en contre partie l'écriture est plus lourde. Par soucis de sécurité, il vaut mieux limiter au strict minimum les variables globales publiques, et faire usage du paramètrage des fonctions, compte tenu des avantages offerts en termes de maintenance et de réutilisation. Remarque : Par défaut, les variables globales (publiques ou privées) sont initialisées à zéro. S'il est nécessaire d'initialiser une variable globale publique à une autre valeur, ceci peut être fait au moment de sa définition, mais non au moment de sa déclaration d'extern.

Page 74: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 73

3 Organisation générale d'un module en langage C:

Afin de répondre aux exigences de l’approche modulaire, et en particulier au principe de l'encapsulation, un module est constitué de deux parties: 1) une partie publique (l'interface) permettant de préciser quels sont les objets du module

(variables, constantes, types ou fonctions) qui pourront être importés par d'autres modules 2) une partie privée (la réalisation) invisible des autres modules contenant la définition des

fonctions (publiques ou privées) et des objets privés (c'est à dire des objets inaccessibles aux autres modules)

l'interface: c'est un fichier dans lequel on précise quels sont les objets du module qui seront importés par d'autres modules (objets publics). Ce fichier contient donc: - la définition des constantes (par #define ou enum ou const); - la définition de nouveaux types (par typedef); - la définition des macro-instructions (par #define); - la prédéclaration ou le prototypage (compilateur C-ANSI) des fonctions publiques (une

procédure est une fonction qui ne retourne rien). Par défaut, une fonction est publique; - la définition des variables globales publiques (allocation mémoire et initialisation

éventuelle) lorsque l'interface est importée dans le fichier réalisation du module en question, ou bien la déclaration de ces variables en tant qu' extern lorsque l'interface est importée dans le fichier réalisation d'un autre module qui utilise ces variables globales

la réalisation: c'est un fichier dans lequel sont spécifiés les objets privés du module. On y trouve également le corps des fonctions publiques ou privées. Ce fichier contient: - la définition des constantes, variables, macro-instructions et types privés; - le prototypage des fonctions privées; - la définition des fonctions privées et des fonctions publiques; Chaque module comprendra donc deux parties sous formes de deux fichiers appelés module.h pour l’interface et module.c pour la réalisation. Un module peut importer l’interface d’un autre module, et invoquer ainsi les fonctions ou procédures publiques proposées par cette interface. Interface: Objets module1.h publics exportés Réalisation: Objets Le module 2.c privés au module importe l’interface procédures et/ou les module1.c module2.c de module1 exportées ou non

Page 75: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 74

L'importation se réalise grâce aux directives d'inclusion conditionnelle offertes par le préprocesseur. Lorsqu'un module utilise des objets publics d'un autre module, il doit donc importer l'interface de ce dernier par une directive d'inclusion de fichier, puisque c'est l'interface qui précise les objets accessibles de l'extérieur. 3-1 L'inclusion conditionnelle: Pour faire des inclusions conditionnelles, et en particulier éviter les inclusions multiples, on utilise les directives destinées au préprocesseur. Celles ci sont précédées de # dans le source C. On utilise les directives suivantes : Définition d’un identificateur

#define IDENTICATEUR Destruction d’un identificateur

#undef IDENTICATEUR Inclusion :

#include<nom_interface> Compilation conditionnelle :

#ifdef IDENTIFICATEUR ou #ifndef IDENTICATEUR < instructions>

#else <instructions>

#endif 3-2 Ossature d’une interface : Le format général d’un contenu d’un fichier d’interface est le suivant:

#ifndef MODULE_H_ /* pour éviter les doubles inclusions */ #include "autre_module.h" /* si module.h importe des objets définis dans un autre_module.h */ /* définition des constantes publiques */ #define CONSTANTE valeur /* construction des types publiques */ typedef type NOUVEAU_TYPE; /* prototypage des fonctions publiques */ type_retourné fonction_publique(type_arguments); #define MODULE_H_ #endif

Page 76: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 75

3-3 Ossature d’une réalisation : Le format général d’un contenu d’un fichier de réalisation est le suivant:

#include <sdtio.h> /* inclusion des headers standards */ #include "module.h" /* inclusion de l’interface du module module.h */ /* définition des constantes privées */ #define CONSTANTE valeur /* construction des types privés */ typedef type NOUVEAU_TYPE; /* définition des variables globales privées */ static variables_globales_privées; /* prototypage des fonctions privées */ static type_retourné fonction_privée type_arguments); /* définition des fonctions publiques */ type_retourné fonction_publique(type_arguments) { ........ } /* définition des fonctions privées */ static type_retourné fonction_privée(type_arguments) { ........ }

4 Production des modules : 4-1 L’utilitaire make sous Unix:

a) Généralités: Pour obtenir un exécutable a partir de différents modules objets on passe par l'utilitaire Unix make. Celui-ci permet de gérer les relations entre plusieurs modules d'un même exécutable et donc de lancer des commandes de compilation ou d’édition de liens lorsqu'une relation est mise en cause. Comme Unix est capable de savoir si un fichier a été modifie grâce à la structure de l'i-node du fichier, l'utilitaire make lance les commandes appropriées pour produire un fichier cible si les causes_dependances sont modifiées.

Page 77: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 76

L'utilisateur doit donc constituer une fichier de paramètrage de nom makefile ou Makefile qui est constitue d'un ensemble de lignes ayant la structure suivante: fichier_cible: causes_dependances ➡ commandes dans lequel fichier_cible est le résultat de commandes si causes_dependances est vrai. S'il y a plusieurs groupes dépendance/commande, ceux-ci vont du général au particulier. Attention: La ligne commandes commence par tabulation et si plusieurs lignes appartiennent à un causes_dependances ou commandes, il faut les terminer par le caractère \ Exemple: Contenu d'un fichier makefile ou l’exécutable pgm est obtenu par compilation séparée des modules saisent.c tableau.c main.c

pgm : main.o tableau.o saisent.o cc -o pgm main.o tableau.o saisent.o -lcurses -lc main.o : main.c tableau.h saisent.h cc -c main.c tableau.o : tableau.c tableau.h cc -c tableau.c saisent.o : saisent.c saisent.h cc -c saisent.c

Pour lance cette compilation séparée, l'utilisateur tape la commande:

make On peut appeler autrement le fichier makefile, dans ce cas la commande à passer est: make -f nom_fichier_makefile

b) Cas des bibliothèques et des headers personnels: Il est possible dans un fichier makefile d'indiquer des bibliothèques personnelles et des headers personnels. Ces derniers peuvent alors être inclus par la directive

#include<mon_header>

Il est également possible d'invoquer un autre make a l’intérieur d'un fichier makefile. Ici dans l'exemple suivant, on compile dans $HOME/system/.ipc/tp.c++ et on veut lancer un make dans le répertoire $HOME/system/.ipc/prsys

Page 78: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 77

Exemple :

LIB=${HOME}/local/lib/liperso.a INCLUDE=${HOME}/local/include CHEMIN=${HOME}/system/.ipc/prsys tp.e: p.o p1.o p2.o (cd $(CHEMIN); \ make -f mkmyprsys.c++) g++ -o tp.e p.o p1.o p2.o $(CHEMIN)/myprsys.o -lc -I$(INCLUDE) p.o: p.cc g++ -c p.cc -I$(INCLUDE) p1.o: p1.cc g++ -c p1.cc -I$(INCLUDE) p2.o: p2.cc g++ -c p2.cc -I$(INCLUDE)

On positionne dans l'exemple deux constantes appelées LIB et INCLUDE. Le contenu d'une constante est rappelle par $(nom_constante). L'option -I permet d'indiquer au compilateur où se trouvent les headers à inclure en plus des chemins standards d'inclusion. Sur la ligne cd $(CHEMIN) le \ est nécessaire pour préciser à make que la ligne n'est pas terminée; les parenthèses servant à préciser que c'est un groupe de fonctions dépendantes les unes des autres.

c) Chemin d’accès aux bibliothèques et headers privés : Pour prendre en compte le chemin $HOME/local/include il faut mettre la macro dans le fichier .profile avec le path complet

export HEADER="$HOME/local/include/" export CFLAGS="-I $HEADER"

on invoque alors le compilateur par :

gcc -c $HOME/local/sources/kbget.c $CFLAGS

d) Compilation avec appel a l’éditeur de lien ld: Il est possible également d’invoquer l’éditeur de liens en dehors de l’option du compilateur. Ceci se fait à l’aide de la commande ld. Voici un exemple :

testlink.e: testlink.o gotoxy.o clrscr.o ld /lib/crt0.o -o testlink.e testlink.o gotoxy.o clrscr.o -lc

testlink.o: testlink.c gcc -c testlink.c

gotoxy.o: gotoxy.c gcc -c gotoxy.c

clrscr.o: clrscr.c gcc -c clrscr.c

Page 79: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 78

4-2 Pour gérer une bibliothèque sous Unix: Les modules objets produits par l’utilisateur peuvent être archivés dans des bibliothèques personnelles. On construit généralement une bibliothèque pour des modules qui traitent d’une même classe de problèmes. Pour archiver un module objet dans un bibliothèque , ou pour remplacer un module objet déjà existant par une nouvelle version, on passe la commande suivante :

ar -r nom_bibliothèque.a nom_module_objet.o Si la bibliothèque nom_bliothèque.a n'existe pas, elle est alors créée. Pour supprimer un module objet d’une bibliothèque, il faut passer la commande :

ar -d nom_bibliothèque.a nom_module_objet.o

Pour visualiser le contenu en termes de modules objet d’une bibliothèque, il faut passer la commande :

ar -t nom_bibliothèque.a

Enfin pour connaître l’ensemble des fonctions intégrées dans les modules objet qui ont été archivés dans une bibliothèque, il faut passer la commande :

nm nom_bibliothèque.a Ces commandes supportant un grand nombre d’options, il est conseillé de faire un man sur ar et nm. 5 Exemple de réalisation d’un module : 5-1 Présentation de l’exemple : Le problème posé à titre d'illustration est simple : il s'agit de traiter un tableau trié d'entiers où chaque élément du tableau est unique, et où les seules opérations possibles sont l'insertion, la suppression et la recherche d'un élément. La recherche d'un élément dans le tableau se fait par dichotomie et retourne le rang de l'élément s'il est présent ou bien ERREUR. Dans dernier cas, la fonction de recherche positionne une variable rang indiquant où il faudrait insérer l'élément. La fonction d'insertion d'un élément vérifie que l'élément n'existe pas déjà et que le tableau n'est pas plein et retourne ERREUR si le tableau est plein, ou le rang auquel l'élément a été inséré. La fonction suppression renvoie OK si l'élément a été supprimé ou bien ERREUR. On cherche ici à réaliser une version universelle d'un module tableau capable de travailler sur n’importe quel tableau d’éléments de type quelconque.

Page 80: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 79

5-2 Les problèmes à gérer : Le fait de vouloir disposer d'un module tableau capable d'opérer sur un tableau de n'importe quel type, pose le problème de la surcharge des opérateurs affectation et comparaison. Un opérateur est surchargé, s'il représente plusieurs implémentations différentes d'une même opération. En C, le même symbole d'affectation est utilisé pour les entiers, les réels ou les caractères (de même pour le symbole comparaison). En revanche, pour les chaînes il faut passer par des opérateurs spécifiques et pour les types construits, cela dépend de la logique de l’application. Compte tenu de ces remarques, il va falloir gérer plusieurs points : 1. ne connaissant pas à priori le type des éléments du tableau (et donc leur taille), il faut

considérer en fait le tableau comme une suite d'octets auxquels on accède par un pointeur générique pouvant pointer sur n'importe quoi. En C-ANSI le type pointeur générique se déclare void *;

2. pour l'affectation, on s’affranchit du problème de représentation interne par la recopie octet

par octet sur la longueur d'un élément grâce à la fonction memcpy. Il faut donc communiquer aux fonctions du module, la taille en octets d'un élément du tableau ;

3. pour décaler d'un rang dans le tableau, on avance (ou on recule) d'un nombre d'octets égal

à la taille mémoire nécessaire à la représentation d'un élément. 4. on ne peut pas comparer deux éléments d'un type donné de la même façon que deux

éléments d'un autre type, compte tenu de leur représentation interne. La comparaison octet par octet n'étant pas possible puisque directement dépendante du type des éléments du tableau, on transmet aux fonctions du module tableau un pointeur sur la fonction de comparaison qu'elles doivent utiliser.

On obtient ainsi une seule version du module tableau, qui une fois compilée, est utilisable quel que soit le type du tableau. Ce module permet aussi de gérer dans le même programme plusieurs tableaux de type différent.

Page 81: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 80

5-3 Contenu de l’interface tableau.h : #ifndef TABLEAU_H_ #define TABLEAU_H_ /*--- Inclusion des headers necessaires -------------------------------*/ #include <stdio.h> #include <stdlib.h> #include <string.h> /*--- Types exportes par le module ------------------------------------*/ typedef enum {ERREUR=-1, OK} Status_TABLEAU; typedef int (* PTRFONC_COMP) (void *, void *); /*--- Fonctions publiques du module -----------------------------------*/ Status_TABLEAU rechercher (void * tab, void *pt_el, int taille_el, int nb_elem, PTRFONC_COMP cmp, int *place); /*=====================================================================*/ /* Descrition: recherche dichotomique d'un element dans un tableau */ /* trie sans presence de doublons */ /* Entrees : tab adresse du tableau */ /* pt_el pointeur sur l'element recherche */ /* taille_el taille d'un element */ /* cmp pointeur sur la fonction de comparaison utilisee */ /* nb_elem nombre d'elemnts presents dans le tableau */ /* E/S : place qui indique le rang de l'element s'il est present */ /* ou le rang de l'element immediatement superieur si */ /* l'element est absent du tableau */ /* Sortie : retourne OK si l'element est present ERREUR sinon */ /*=====================================================================*/ int inserer(void * tab, void *pt_el, int taille_el, int *ptr_nb_elem, PTRFONC_COMP cmp); /*=====================================================================*/ /* Descrition: insere un element dans un tableau trie sans doublons */ /* Entrees : tab adresse du tableau */ /* pt_el pointeur sur l'element a inserer */ /* taille_el taille d'un element */ /* cmp pointeur sur la fonction de comparaison utilisee */ /* E/S : ptr_nb_elem pointeur sur le nombre d'elements presents */ /* dans le tableau. Ce nombre est incremente si l'insertion*/ /* a eu lieu (element non deja present) */ /* Sortie : retourne ERREUR si l'element est deja present */ /* sinon retourne le rang ou a eu lieu l'insertion */ /* Exception : cette fonction ne gere pas le depassement de capacite */ /* du tableau (ceci doit etre verifie par l'appelant) */ /*=====================================================================*/ int supprimer(void * tab, void *pt_el, int taille_el, int *ptr_nb_elem, PTRFONC_COMP cmp); /*=====================================================================*/ /* Descrition: supprime un element dans un tableau trie sans doublons */ /* Entrees : tab adresse du tableau */ /* pt_el pointeur sur l'element a supprimer */ /* taille_el taille d'un element */ /* cmp pointeur sur la fonction de comparaison utilisee */ /* E/S : ptr_nb_elem pointeur sur le nombre d'elements presents */ /* dans le tableau. Ce nombre est decremente si la */ /* suppression a eu lieu (element present) */ /* Sortie : ERREUR si element absent ou tableau vide */ /* sinon retourne le rang ou a eu lieu la suppression */ /*=====================================================================*/ #endif

Page 82: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 81

5-4 Contenu de la réalisation tableau.c : #include "tableau.h" /*---------------------------------------------------------------------*/ Status_TABLEAU rechercher(void *t, void *pt_el, int taille_el, int nb_elem, PTRFONC_COMP cmp, int *place ) { char *tab=(char *)t; int milieu, b_sup=nb_elem-1, b_inf=0, result_cmp; while (b_sup>=b_inf) { milieu=(b_sup+b_inf)/2; result_cmp=(* cmp)(pt_el,tab+(milieu*taille_el)); if (result_cmp<0) b_sup=milieu-1; else if (result_cmp>0) b_inf=milieu+1; else { *place=milieu; return(OK); } } *place = b_inf; return(ERREUR); } /*---------------------------------------------------------------------*/ int inserer (void * t, void *pt_el, int taille_el, int *ptr_nb_elem, PTRFONC_COMP cmp) { char *tab=(char *)t; int i,ind=0; if (rechercher(tab,pt_el,taille_el,*ptr_nb_elem,cmp,&ind)==OK) return ERREUR; for(i=(*ptr_nb_elem);i>ind;i--) memcpy(tab+(i*taille_el),tab+((i-1)*taille_el),taille_el); memcpy(tab+(ind*taille_el),pt_el,taille_el); (*ptr_nb_elem)++; return ind; } /*---------------------------------------------------------------------*/ int supprimer (void * t, void *pt_el, int taille_el, int *ptr_nb_elem, PTRFONC_COMP cmp) { char *tab=(char *)t; int i,ind=0; if (rechercher(tab,pt_el,taille_el,*ptr_nb_elem,cmp,&ind)==ERREUR) return ERREUR; for(i=ind;i<(*ptr_nb_elem)-1;i++) memcpy(tab+(i*taille_el),tab+((i+1)*taille_el),taille_el); (*ptr_nb_elem)--; return ind; }

Page 83: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 82

5-5 Exemples d’utilisation du module tableau: On donne ici les éléments particuliers qui permettent d'utiliser la version universelle du module tableau dans plusieurs cas de types de données.

a) Cas d’entiers: les définitions : #define TAILLE 10 /* taille du tableau */ #typedef int TYPE_TAB /* type des éléments du tableau */ TYPE_TAB tab[TAILLE]; /* le tableau */ TYPE_TAB elem; /* un élément du tableau */ int nb_elem; /* nombre d’éléments du tableau */ - les appels aux fonctions du module tableau : insert(tab, &elem, sizeof(elem),&nb_elem, (PTRFONC_COMP) compint); suppress(tab, &elem, sizeof(elem), &nb_elem, (PTRFONC_COMP) compint); dichoto(tab, &elem, sizeof(elem), nb_elem, (PTRFONC_COMP) compint, &rang); - la fonction de comparaison utilisée : int compint(TYPE_TAB *x, TYPE_TAB*y) { return *x - *y; }

b) Cas de chaînes de caractères: - les définitions : #define TAILLE 20 #typedef char * TYPE_TAB TYPE_TAB tab[TAILLE]; TYPE_TAB elem; int nb_elem; - les appels aux fonctions du module tableau insert(tab, &elem, sizeof(elem),&nb_elem, (PTRFONC_COMP) compch); suppress(tab, &elem, sizeof(elem),&nb_elem, (PTRFONC_COMP) compch); dichoto(tab, &elem, sizeof(elem),nb_elem, (PTRFONC_COMP) compch, &rang); - la fonction de comparaison utilisée static int compch(TYPE_TAB *x, TYPE_TAB *y) { return strcmp(*x, *y); }

Page 84: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 83

c) Cas d’un tableau de pointeurs sur structures: - les définitions #define TAILLE 10 typedef struct produit { char numero[5]; char libelle[25]; float prix; } * TYPE_TAB; TYPE_TAB tab[TAILLE]; TYPE_TAB elem; int nb_elem; - les appels aux fonctions du module tableau insert(tab, &elem, sizeof(elem),&nb_elem, (PTRFONC_COMP) compptstr); suppress(tab, &elem, sizeof(elem),&nb_elem, (PTRFONC_COMP) compptstr); dichoto(tab, &elem, sizeof(elem),nb_elem, (PTRFONC_COMP) compptstr, &rang); - la fonction de comparaison utilisée: static int compptstr(TYPE_TAB *x, TYPE_TAB *y) { return strcmp((*x)->numero,(*y)->numero); }

Page 85: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 84

CHAPITRE 7 - DU C-ANSI AU C++ EN PROGRAMMATION PROCEDURALE Le langage C++ est un sur-ensemble du langage C-ANSI qui permet d'effectuer une programmation orientée objet. Il est aussi presque entièrement compatible avec le C-ANSI en ce sens qu'il supporte la programmation procédurale et la programmation modulaire telles que vues dans les chapitres précédents. Lorsque le langage C++ n'est pas utilisé en programmation orientée objet, c'est à dire lorsque le développement ne fait pas appel aux mécanismes objet (encapsulation, polymorphisme, héritage,....), on parle alors de C++ en mode procédural. Cependant même dans ce mode de programmation non orienté objet, le langage C++ apporte un certain nombre d'enrichissements par rapport au langage C qui sont présentés dans ce chapitre. 1 Commentaires: En plus du style de commentaires du C-ANSI basé sur l’utilisation des séquences de caractères /* et */, le C++ introduit une nouvelle séquence // qui indique un commentaire par ligne. Exemple : // commentaire valable jusqu'à fin de ligne // autre commentaire

Il est possible d’utiliser les deux styles de commentaires, mais chacun est traité de manière indépendante. Ainsi la séquence suivante donne une erreur : // commentaire de ligne suivi de /* bloc de commentaires et fin du bloc de commentaires */

2 Gestion des entrées-sorties: Le C++ reste compatible avec le C-ANSI. A ce titre il est possible d’utiliser les fonctions d’entrées-sorties, déclarées dans <stdio.h>. Cependant le C++ introduit une facilité par rapport à l’utilisation de ces fonctions dans lesquelles l’utilisateur doit préciser le format de conversion. Il permet en effet un traitement uniforme pour les types primitifs grâce aux opérateurs d’insertion dans un flux de sortie et d’extraction dans un flux d’entrée. Parmi les flux, il en existe des prédéfinis qui correspondent au clavier (cin), à l’écran (cout) et le périphérique standard d’erreur (cerr).

Page 86: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 85

Le flux standard de sortie est cout et l’opérateur associé est "<<". Le flux standard d’entrée est cin et l’opérateur associé est ">>". Pour utilisez ces flots de données, il faut inclure le header <iostream.h> Exemple : #include <iostream.h> cin >> variable; // pour la saisie cout << "\ncommentaire" << variable<<"\n"; // pour l'affichage

3 Prototypage : Le C++ impose le prototypage des fonctions qui vont être utilisées ainsi que le type des arguments passés et le type de la valeur retournée. Ceci n’est guère différent du langage C norme ANSI, à la différence qu’une fonction qui ne reçoit pas de paramètres peut ne pas comporter le qualificatif void. En réalité, l'absence de paramètres est interprété différemment: en C-ANSI cela signifie un nombre variable de paramètres alors qu'en C++, cela signifie aucun paramètre. On peut faire apparaître le nom des paramètres dans le prototype d'une fonction. Bien qu’il soit ignoré au niveau du prototypage, ce mécanisme améliore cependant la lisibilité. Exemple : void affiche(int * tab); char sais_car() ; 4 Déclaration et portée des variables: Outre le principe de déclaration des variables du C (globale à plusieurs module, globale à un seul module, locale à une fonction, rémanente ou non), le C++ introduit en plus la possibilité de déclarer un objet à l'intérieur d'un bloc. Un bloc est délimité par le caractère { et le caractère }. La visibilité (ou portée) de la variable est alors limitée au bloc. Elle est détruite à la sortie du bloc. for(int i=0;i<n;i++) { tab[i]=i ; }

Ce mécanisme permet au programmeur de ne déclarer une variable qu’au moment où il en a besoin.

Page 87: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 86

5 Qualificatif const: Le qualificatif const spécifie qu'un objet ne peut pas être modifié. Il permet de s’assurer que toute tentative de modification d’un objet peut être détectée au moment de la compilation. Un identificateur déclaré const doit être initialisé au moment de sa définition. En C++, la portée d’un identificateur déclaré const est limitée au bloc, à la fonction ou au module où il est défini. Exemples : const int n=10; // une constante doit être initialisée n++; // interdit const int *p; // p est un pointeur sur entier constant p=&n; // autorisé car n de type const int p=&i; // interdit si i déclaré int i *p++; // interdit car *p est constant *p=valeur; // interdit car *p est constant int * const p2=&i; // p2 est un pointeur constant sur entier;

// il doit être initialisé à la déclaration p2=p; // interdit car p2 est constant p2=&j; // avec j de type int interdit *p2=j; // autorisé cela revient à faire i=j const int * const p3 = &a; // p3 est un pointeur constant sur entier // constant; il doit être initialisé; ici a

// est de type const int // a et p3 ne peuvent pas être modifiés

int *pi; // pi pointeur sur entier pi=&n; // interdit: on ne peut modifier n via pi const int *pj; // pj pointeur sur entier constant pj=&n; // maintenant autorisé

6 Définition de type enum et struct: Il n'est plus nécessaire en C++ de passer par le qualificatif typedef pour les énumérations et les structures, car les types de données définis à l’aide de struct et enum se comportent comme des types de base.. Exemples: enum STATUS {OK, ERREUR}; // STATUS est maintenant un type

Page 88: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 87

struct NOEUD { // NOEUD est un type, donc il peut char info[20]; NOEUD *g; // être référencé dans la définition NOEUD *d; // récursive de NOEUD } n1; // n1 est une variable de type NOEUD

7 Gestion dynamique de la mémoire: Les fonctions d’allocation mémoire malloc() et de libération mémoire free() sont remplacées en C++ par deux opérateurs new et delete plus simples d'emploi. Exemples: pi = new int; // pi pointe sur un entier t = new type_voulu[20]; // t pointe sur un tableau delete pi; delete [] t;

8 Spécification inline: Il s'agit d'un mode de définition de fonction en expansion, c'est à dire que le code de la fonction est incorporé dans le programme chaque fois que la fonction est invoquée. Ce mécanisme est similaire à celui des macro substitutions en C-ANSI, mais il a l’avantage d’éviter les effets de bord rencontrés avec certains arguments lors de l’expansion de la macro substitution. Exemple: La macro #define abs(x) (((x)>0) ? (x) : -(x)) devient inline int abs(int x) { return (x>0 ? x : -x)

}

Page 89: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 88

9 Type référence: 9-1 Définition: Le C++ introduit le type référence. Une référence désigne un emplacement mémoire correspondant à une variable d’un type donné. Il s’agit d’un alias d’identificateur qui doit être associé à une variable. Une référence est donc initialisée au moment de sa déclaration. On utilise le caractère & pour désigner une référence. Exemple: int i ; int &j =i ; // maintenant j est un synonyme de i int k=0 ; i=3 ; // donc j vaut 3 j++ ; // donc i et j valent 4 j=k ; // i et j valent maintenant 0 Le type référence est principalement utilisé pour le passage des arguments d’une fonction et comme valeur de retour d’une fonction. 9-2 Passage d’argument à une fonction par référence: Il existe trois mécanisme de passage d'arguments en C++. En effet, le C++ introduit en plus des passages de paramètres habituels du C-ANSI, un nouveau mécanisme dit passage par référence (identique au langage PASCAL). Lorsqu’on indique un passage par référence, le corps de la définition de la fonction ne change pas par rapport à un passage par valeur, mais les arguments passés par référence peuvent alors être modifiés, puisque la fonction agit alors sur leurs synonymes. Il s'agit d'une écriture allégée, mais qui masque la mise en œuvre que l'on voit en C-ANSI avec le passage de pointeurs, puisqu’on n’utilise plus le mécanisme d’indirection imposé par l’usage des pointeurs. Rien ne permet dans le corps de la fonction de faire la différence entre un passage des paramètres par référence ou par valeur. Or le comportement est très différent suivant le cas. Il faut donc regarder attentivement le prototype de la fonction pour savoir s’il y a modification possible ou non suivant le type de passage utilisé. Cependant le passage par référence comme dans le cas d’un passage explicite par pointeur, permet d’éviter la recopie dans la pile, d’arguments de taille importante. Dans ce cas, si on veut utiliser ce type de passage pour des soucis de performances mais sans modification possible de ou des arguments, il faut le préciser par le qualificatif const.

Page 90: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 89

Exemple : fonction qui permet de permuter deux entiers 1) en C-ANSI (passage par pointeurs)

void echange(int *, int *); // prototype void echange(int *x, int *y) { // définition

int c = *x; *x = *y ; *y = c ;

} echange(&a,&b); // appel

2) en C++ (passage par référence)

void echange(int &x, int &y); // prototype void echange(int &x, int &y) { // définition

int c=x; x=y; y=c;

} echange(a,b); // appel

Les différents types de passage peuvent être panachés dans une même fonction. Exemples : void inser(const int &x, int const *t2) ;

void recopie(int* const & t1, int *t2); void charge(const int * const t1, int * const t2);

9-3 Retour par référence d’une fonction: Une fonction peut retourner une valeur ou un pointeur. En C++, il est possible de retourner également une référence vers un objet. On peut alors soit affecter un objet de même type par l’objet retourné, soit modifier l’objet lui-même retourné par la fonction en tant que lvalue. Exemple : On considère une fonction (sans grand intérêt d’ailleurs mais qui illustre le propos) decal qui retourne une référence sur un type entier. Soient les définitions suivantes :

const int TAILLE=5; int t[TAILLE]={2,8,7,9,3};

int & decal(int * const tab, const int & i){

if (i>0 && i<TAILLE) return tab[i-1]; else return tab[0];

}

Page 91: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 90

On peut utiliser cette fonction de deux manières :

x=decal(t,4); // x devient alors égal à t[3] donc x = 9 decal(t,2)=5; // t[1] devient égal à 5

decal(t,2)=decal(t,4) // t[1] devient égal à t[3] c’est à dire 9

10 Valeurs par défaut des arguments d’une fonction: Une nouvelle possibilité offerte par le C++ par rapport au C est de pouvoir spécifier la valeur par défaut que peuvent prendre certains arguments dans l’appel d’une fonction. Ceci est particulièrement utile lorsque l’usage d’une fonction nécessite souvent une même valeur d’un ou plusieurs paramètres au moment de l’appel. Les valeurs par défaut doivent être précisées au moment du prototypage de la fonction. Les arguments par défaut doivent être en dernier et de façon consécutive dans la liste des arguments. Exemple : On considère une fonction de décalage à gauche d’un entier qui par défaut décale cet entier d’une position binaire.

void decalage_a_gauche(int &x, int nb=1); // prototypage

void decalage_a_gauche(int &x, int nb) { // définition x=x<<nb;

}

On peut applique cette fonction sur la variable control

int control=0x7f; // définition de la variable

decalage_a_gauche(control); // on décale d’une position à gauche cout<<" control = "<<hex<<control<<endl; // control = FE

decalage_a_gauche(control,3); // on décale de 3 positions à gauche cout<<" control = "<<hex<<control<<endl; // control = 07 F0

Si on prototype la fonction de la sorte

void decalage_a_gauche(int nb=1, int &x)

le compilateur génère un message d’erreur.

Page 92: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 91

11 Surcharge des fonctions: La surcharge d’une fonction consiste à définir (et donc pouvoir appliquer) la même fonction sur des types différents. Ce mécanisme est particulièrement intéressant lorsqu’on veut traduire un même algorithme en s’affranchissant des problèmes liés à la représentation interne des objets sur lesquels portent le traitement. Exemple : // insertion d’un entier dans un tableau d’entiers int inserer(int *t, int el, int &nb, int taille); // insertion d’un reel dans un tableau de reel int inserer(float *t, float el, int &nb, int taille); // recuperation d’un entier par son rang dans un tableau d’entiers int recuperer(int *t, int rang, int nb); // recuperation d’un réel par son rang dans un tableau de réels float recuperer(float *,int rang, int nb);

Le choix entre les différentes versions d’une même fonction se fait au moment de la compilation (on parle de résolution statique) grâce à un mécanisme de signature, basé sur le type des arguments. Pour résoudre l’appel à la bonne fonction, le compilateur recherche :

1. une correspondance exacte entre les types des arguments formels et effectifs 2. une correspondance mettant en œuvre les conversions standards de types même

éventuellement dégradantes 3. une correspondance mettant en œuvre les conversions définies par l’utilisateur

Attention : 1. les règles de conversion peuvent introduire une ambiguïté

int f(int, float) ; int f(float,int) ; // appel f(5,1) ambigu

2. les arguments par défaut également int f(float,int=0) ; int f(float) ; // appel f(5) ambigu

3. deux fonctions surchargées ne peuvent être discriminées uniquement par le type retourné par la fonction

int f(float) ; float f(float) ; // erreur !

Page 93: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 92

12 Conversion de type: Les règles de promotion sont identiques au C pour les types primitifs. Pour les conversions explicites, on peut utiliser la notion d’opérateur de coercition du C. Cependant, le C++ ajoute la notation dite fonctionnelle qui est de la forme Type (valeur ou expression)

Cette notation est pratique en particulier pour les types construits. Exemple :

typedef int * PTI ; typedef char * PTC ;

PTI pi ; PTC pc ;

pc=(char *i)pi ; // cast operator en C ou encore pc=(PTC)pi pc=PTC (pi) ; // notation fonctionnelle en C++

13 Appel à des fonctions C-ANSI: Le C++ réalisant une signature des fonctions, on ne peut réutiliser directement des modules objets compilés en C-ANSI. Pour contourner ce problème, il faut préciser au compilateur C++ par la clause extern "C" les fonctions qui ont été compilées en C-ANSI. Pour cela, il suffit d’inclure cette clause conditionnellement dans l’interface des modules écrits en C-ANSI. Le système définit la constante __STDC__ pour l'environnement C_ANSI et la constante __cplusplus pour l'environnement C++. Donc le prototypage des fonctions d'une interface écrite en C doit être encadré par les séquences #ifdef __cplusplus // environnement C++ extern "C" { /* sinon environnement C-ANSI */ #endif Prototype_fonction............... #ifdef __cplusplus } #endif

Page 94: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 93

Page 95: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 94

ANNEXE A - LA BIBLIOTHEQUE STANDARD 1 Introduction : Le langage C proprement dit, ne comprend pas d'opérations spécialisées telles que la gestion des entrées-sorties, la traitement des chaînes de caractères, les accès aux fichiers, la gestion de la mémoire, l’interface avec le système d’exploitation. Le mécanisme d’enrichissement du langage se fait grâce à des bibliothèques de modules objets. Les modules objets contiennent eux-mêmes des fonctions, chaque fonction effectuant pour le compte du programmeur un certain traitement suivant les spécifications de la dite fonction. Pour utiliser les services d’une fonction archivée dans une bibliothèque il faut: 1. inclure par un #include le fichier d’en-tête .h prototypant la fonction et définissant les

types ou constantes spécifiques aux fonctions; on trouvera également dans les fichiers d'en-tête des définitions de macros

2. mettre en ligne au moment de l’édition des liens la bibliothèque en question Parmi les bibliothèques qui peuvent être utilisées, il existe la bibliothèque standard appelée libc.a qui est automatiquement mise en ligne. Cette annexe présente les principales fonctions de cette bibliothèque ainsi que les fichiers d'en-tête (ou headers) à inclure pour l’utiliser. Les principaux fichiers d'en-tête standards sont: <assert.h> vérification d'assertions <ctype.h> traitement des caractères <errno.h> conditions d'erreurs <float.h> limites de représentation en virgules flottantes <limits.h> limites de représentation des types de base <locale.h> localisation des applicatifs <stdarg.h> fonctions à nombre d'arguments variables <stdlib.h> utilitaires généraux <setjmp.h> sauvegardes et restaurations de contexte d'exécution <signal.h> traitement des signaux <stddef.h> définitions communes <stdio.h> entrées-sorties standard <string.h> traitement des chaînes de caractères <time.h> date et heure <times.h> temps système

Page 96: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 95

2 Les fonctions d’entrées-sorties: Ces fonctions nécessitent l’inclusion du fichier <stdio.h> Une entrée-sortie correspond à un flot (ou flux) qui peut être associée à un périphérique. Ce flot peut être en mode texte ou en mode binaire. Un flot en mode texte est une suite de lignes, un ligne étant une suite caractères terminée par le marqueur fin de ligne '\n'. Un flot binaire est une suite d'octets représentant des données internes. Un flux est représenté par le type prédéfini FILE. Trois flux spéciaux stdin, stdout et stderr sont automatiquement ouverts, et correspondent à l’entrée standard, la sortie standard, et la sortie des erreurs. Les fonctions suivantes concernent le traitement des fichiers. Le type size_t est le type entier non signé retourné par l'opérateur sizeof. FILE *fopen (const char *nom_fic, const char *mode) ouvre le fichier nom_fic et retourne un flux, ou NULL si la tentative échoue ; l'argument mode peut prendre les valeurs :

"r" ouverture en lecture d’un fichier texte "w" effacement et ouverture en écriture d’un fichier texte "a" ouverture et écriture en fin de fichier d’un fichier texte "r+" ouverture en lecture et écriture d’un fichier texte "w+" effacement et ouverture en lecture écriture d’un fichier texte "rb" ouverture en lecture d’un fichier binaire "wb" effacement et ouverture en écriture d’un fichier binaire "ab" ouverture et en écriture fin de fichier d’un fichier texte "rb+"ouverture en lecture et écriture d’un fichier binaire "wb+"effacement et ouverture en lecture écriture d’un fichier binaire "ab+"ouverture en lecture et écriture en fin d’un fichier binaire

FILE *freopen (const char *nom_fic, const char *mode,FILE *stream) remplace le flot stream par l’ouverture du fichier nom_fic suivant mode, et renvoie le nouveau flux ou NULL en cas d'erreur. int fflush (FILE *stream) écrit le contenu de la mémoire tampon associée au flux stream, et retourne EOF en cas d’erreur et 0 sinon. int fclose (FILE * stream) écrit le contenu de la mémoire tampon associée au flux stream, et ferme le flot ; retourne EOF en cas d’erreur et 0 sinon. int remove (const char *nom_fic) efface le fichier nom_fic sur le disque ; retourne 0 si la fonction s’est bien déroulée.

Page 97: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 96

int rename (const char *source, const char *dest) renomme le fichier source en dest si ils sont sur le même système de fichiers; retourne 0 si elle s’est bien passée. FILE * tmpfile (void) crée un fichier temporaire en générant un nom de fichier grâce à la fonction tmpnam ; retourne le flot associé au fichier ouvert ou NULL si erreur. Le fichier automatiquement détruit lors de terminaison du processus. char *tmpnam(char * chaine) génère un nom de fichier qui n'est pas le nom d'un fichier existant en utilisant le chemin P_tmpdir prédéfinie dans <stdio.h>; si chaine est égale à NULL, la fonction retourne un pointeur sur un tableau statique interne où est stocké le nom du fichier ; sinon s doit être d'une taille au moins égale à L_tmpnam caractères (L_tmpnam étant prédéfini dans <stdio.h>)et la fonction retourne s ; à chaque appel un nom différent est généré. int fscanf (FILE *stream, const ch2r *format, arg1 ... argn ) lit à partir du flux stream les arguments arg1..argn en les décodant sous le contrôle format ; chacun des arguments doit être un pointeur sur une zone mémoire où sera rangé la donnée décodée ; la fonction renvoie EOF si la fin du fichier est détectée ou si il y a une erreur, sinon elle retourne le nombre d’arguments reconnus ; format précise la conversion à effectuer et est exprimé sous la forme :

%*nlX avec : ‘*‘ : ne pas transférer le résultat obtenu dans la donnée ‘n’ : taille maximale du champ à décoder ‘l’ : indique si la donnée est au format long ‘X’ : type du format de conversion : d entier signé en décimal u entier non signé en décimal x entier en hexadécimal

o entier en octal s chaîne de caractères c caractère e,f,g réel La chaîne format peut contenir également des espaces ou des caractères de tabulation qui sont ignorés, et des caractères ordinaires (différents de %) qui doivent correspondre au caractère lu. int scanf (const char *format, . . . ) saisie des données formatées à partir de l’entrée standard ; cette fonction est équivalente à fscanf(stdin,.......) int sscanf (const char *s, const char *format, . . . ) fonction équivalente à fscanf mais les données sont extraites de la chaîne s. int fgetc (FILE * stream) retourne le premier caractère disponible sur le flux stream, lu comme un unsigned char (converti en int), ou bien EOF si la fin de fichier est atteinte ou si une erreur survient.

Page 98: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 97

int getc(FILE *stream) macro équivalente à la fonction fgetc. int getchar(void) lit un caractère depuis l’entrée standard ; équivaut à getc(stdin). int ungetc(int c, FILE *stream) replace la caractère c (converti en unsigned char) sur le flot ; on ne peut remettre EOF dans le flot ; retourne le caractère remis, ou bien EOF en cas d'erreur. char *fgets (char *s, int max, FILE *stream) lit au plus max-1 caractères sur le flux stream et les place dans chaîne s ; la lecture s'arrête si un caractère de fin de ligne '\n' est détecté ou la fin du fichier; le caractère '\0' est placé à la fin de s ; retourne s ou bien NULL si la fin de fichier est atteinte ou si une erreur survient. char *gets(char *s) lit une ligne sur le flux d’entrée standard stdin et la place dans la chaîne s; le caractère '\n' de fin de ligne est remplacé par'\0' ; retourne s, ou bien NULL si la fin de fichier est atteinte ou si une erreur survient. int fprintf (FILE *stream, const char *format, arg1 ... argn ) écrit sur le flux stream en les décodant sous le contrôle de format les arguments arg1..argn ; retourne le nombre de caractères écrits sur la sortie ou bien EOF en cas d'erreur ; format précise la conversion à effectuer et est exprimé sous la forme : %-m.plX avec :

‘-‘ : justification à droite (gauche par défaut) ‘m’ : taille minimum de la zone ‘.p’ : taille de la partie décimal pour un réel ou taille maxi pour une chaîne ‘l’ : indique si la donnée est au format long ‘X’ : type du format de conversion : d entier signé en décimal u entier non signé en décimal x entier en hexadécimal

o entier en octal s chaîne de caractères c caractère f réel e réel en notation avec exposant g réel avec conversion en %d %e ou %f int printf(const char *format, ...) affiche des données formatées sur la sortie standard ; cette fonction est équivalente à fprintf(stdout,.......) int sprintf(char *s, const char *format, ...) fonction équivalente à fprintf mais le résultat est alors écrit dans la chaîne s terminée par '\0' ; retourne le nombre de caractères écrits sans compter '\0'.

Page 99: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 98

int fputc(int c, FILE *stream) écrit le caractère c (converti en unsigned char) sur le flot stream. Elle retourne le caractère écrit, ou bien EOF en cas d'erreur. int putc(int c, FILE *stream) macro équivalente à la fonction fputc. int putchar(int c) équivaut à putc(c,stdout). int fputs(const char *s, FILE *stream) écrit la chaîne s sur le flot stream; retourne EOF en cas d'erreur. int puts(const char *s) écrit la chaîne s et le caractère '\n' sur stdout ; retourne EOF en cas d'erreur. size_t fread(void *buff, size_t taille, size_t nb, FILE *stream) lit au plus nb objets de taille octets sur le flux stream et les place dans le buffer pointé par buff ; retourne le nombre d'objets lus, 0 en cas d’erreur ou de fin de fichier ; il faut utiliser feof et ferror pour déterminer l'état du flot. size_t fwrite(void *buff, size_t taille, size_t nb, FILE *stream) écrit nb objets de taille octets sur le flux stream à partir du buffer pointé par buff ; retourne le nombre d'objets écrits, 0 en cas d’erreur ou de fin de fichier ; il faut utiliser feof et ferror pour déterminer l'état du flot. int fseek (FILE *stream, long offset, int origine) positionne le pointeur de fichier sur le flot stream pour la prochaine lecture ou écriture ; le déplacement se fait de offset octets à partir de :

le début de fichier si origine = SEEK_SET (0) la position courante si origine = SEEK_CUR (1) la fin du fichier si origine = SEEK_END (2)

Le déplacement peut être négatif ; la fonction retourne 0 si elle s'est déroulée correctement, et -1 en cas d’erreur. int ftell (FILE *stream) retoume la position courante sur le flot stream en nombre d’octets depuis le début du fichier, ou bien -l en cas d'erreur. void rewind (FILE *stream) positionne le pointeur d’emplacement au début du flux stream. int fgetpos (FILE *stream, fpos_t *ptr) sauvegarde la position courante du flot stream dans * ptr; retourne -l en cas d'erreur. int fsetpos(FILE *stream, const fpos_t *ptr) positionne la position du pointeur d’emplacement du flot stream à la valeur mémorisée dans *ptr par l’appel à la fonction fgetpos; retourne -l en cas d'erreur.

Page 100: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 99

void clearerr(FILE *stream) remet à zéro les indicateurs de fin de fichier et d'erreur associés au flux stream. int feof (FILE *stream) retourne une valeur différente de 0 si la fin du fichier du flot stream a été détectée. int ferror(FILE *stream) retourne 0 si aucune erreur n’a été détectée lors d’une lecture ou écriture sur le flot stream. void setbuf (FILE *stream, char *tampon) détermine un tampon d’entrées sorties tampon utilisé pour chaque lecture écriture sur le flux stream ; il faut l'appeler avant la première lecture ou écriture sur ce flot ; si tampon vaut NULL, alors les entrées sorties ne seront plus bufferisées sinon la taille de tampon est égale à la constante BUFSIZE définie dans <stdio.h> int setvbuf (FILE *stream, char *tampon, int mode, size_t taille) définit le mode de bufferisation de tampon sur le flux stream suivant les valeurs de mode : _IOFBF : bufferisation complète

_IOLBF :bufferisation par ligne (tampon plein, appel à fflush, fin de ligne..) _IONBF :aucune bufferisation

Si tampon est différent de NULL, il pointe sur un tableau de taille octets utilisé comme buffer; la fonction retourne 0 s’il n’y a pas d’erreurs. 3 Les opérations de traitement de chaînes de caractères : Une chaîne de caractères en C est une suite d’octets terminée par le caractère '\0'. Une chaîne est du type char *. Pour utiliser les fonctions de traitement de chaînes de la bibliothèque standard, il faut inclure le header <string.h> char *strcpy(char *s1, char *s2) copie la chaîne s2 dans la chaîne s1 et retourne s1. char *strncpy(char *s1, char *s2, int n) copie au plus n caractères de la chaîne s2 dans la chaîne s1 et retourne s1. char *strcat(char *s1, char *s2) concatène la chaîne s2 à la suite de la chaîne s1 et retourne s1 ; char *strncat(char *s1, char *s2, int n) concatène au plus n caractères de la chaîne s2 à la suite de la chaîne s1 ; retourne s1 ;

Page 101: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 100

int strcmp(char *s1, char *s2) compare la chaîne s2 à la chaîne s1 et retourne : <0 chaîne s1 < chaîne s2

=0 chaîne s1 = chaîne s2 >0 chaîne s1 > chaîne s2

int strncmp(char *s1, char *s2, int n) même fonction que strcmp mais la comparaison se fait au plus sur les n premiers caractères. char *strchr(char *s1, char c) retourne un pointeur sur la première occurrence du caractère c dans la chaîne s1 ou NULL si le caractère n’est pas trouvé. char *strrchr(char *s1, char c) même fonction que strchr mais on cherche la dernière occurrence du caractère c. size_t strspn(char *s1,char *s2) renvoie la longueur de la plus grande chaîne de s1 constituée uniquement des caractères appartenant à la chaîne s2. size_t strcspn(char *s1,char *s2) renvoie la longueur de la plus grande chaîne de s1 constituée uniquement des caractères n’appartenant pas à la chaîne s2. char *strstr(char *s1,char *s2) renvoie un pointeur sur la première occurrence de la chaîne s2 dans la chaîne s1, ou bien NULL si s2 n’appartient pas à s1. char *strrstr(const char *s1, char *s2) renvoie un pointeur sur la dernière occurrence de la chaîne s2 dans la chaîne s1, ou bien NULL si s2 n’appartient pas à s1. char * strpbrk(char *s1, char *s2) renvoie un pointeur sur la première occurrence dans la chaîne s1 d’un des caractères de la chaîne s2, ou bien NULL si aucun caractère n’a été trouvé. char *strdup(char *s) renvoie une chaîne identique à la chaîne s. L’espace mémoire est alloué par l’appel à la fonction malloc(). size_t strlen(s) renvoie la longueur de la chaîne s, le caractère '\0' n’étant pas compter. char *strtok(char *s1, char *s2) découpe la chaîne s1 par des chaînes séparées grâce à des caractères de s2. Chaque fois qu’un caractère de s1 appartient à s2, la fonction renvoie la chaîne des caractères précédents. Si on invoque plusieurs fois la fonction en désirant garder la position d’arrêt de l’appel précédent, il faut mettre s1 à NULL. La fonction retourne NULL lorsqu’il n’y a plus de sous-chaînes à trouver.

Page 102: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 101

4 Les opérations de traitement de caractères : Il s’agit de fonctions et de macros de classification et de conversion de caractères qui nécessitent l’inclusion du fichier d’en-tête <ctype.h>. Le caractère à tester ou à convertir appartient à l'ensemble des caractères ASCII (unsigned char) ou peut être égal à EOF qui est codé -1. C'est pourquoi ces fonctions prennent le caractère comme un argument de type int. Lorsque l'argument testé remplit la condition indiquée, la valeur de retour de type int est non nulle ("vrai"), zéro sinon ("faux"). int isascii(int c) vrai si le caractère c est un caractère du code ASCII (de 0x00 à 0x7F). int isalnum(int c) vrai si le caractère c est une lettre ou un chiffre. int isalpha(int c) vrai si le caractère c est une lettre minuscule ou majuscule. int iscntrl(int c) vrai si le caractère c est un caractère de contrôle. int isdigit(int c) vrai si le caractère c est un chiffre. int isxdigit(int c ) vrai si le caractère c est un chiffre hexadécimal int isgraph(int c) vrai si le caractère c est un caractère affichable différent de l'espace (de 0x21 à 0x7E). int islower(int c) vrai si le caractère c est une lettre minuscule. int isupper(int c) vrai si le caractère c est une lettre majuscule int isprint(int c) vrai si le caractère c est un caractère imprimable (de 0x20 à 0x7E). int ispunct(int c) vrai si c est un caractère de ponctuation c'est à dire autre qu'une lettre, un chiffre ou l'espacement. int isspace(int c) vrai si le caractère c génère un espace (espace, saut de page, fin de ligne, retour chariot, tabulation, tabulation verticale).

Page 103: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 102

int tolower(int c) retourne la caractère c convertit en minuscules si c est une lettre en majuscule, sinon la fonction retourne le caractère inchangé. int toupper(int c) retourne la caractère c convertit en majuscules si c est une lettre en minuscule, sinon la fonction retourne le caractère inchangé. int toascii(int c) retourne la caractère c convertit en code ASCII, c'est à dire débarrassé des bits qui ne participent pas au codage ASCII. 5 Les opérations de traitement de chaînes d'octets: Il s'agit d'une famille de fonctions qui servent à manipuler des chaînes de caractères mais non terminées par le caractère '\0'. Elles sont vues comme des tableaux de caractères en mémoire centrale, et on doit toujours indiquer explicitement la longueur à traiter. Elles nécessitent l'inclusion du header <string.h>. void *memcpy(void *s1, void *s2, size_t n) recopie n octets de la zone mémoire pointée par s2 dans la zone mémoire pointée par s1, et retourne un pointeur sur s1. Si les zones se chevauchent en mémoire, le comportement est indéfini. void *memccpy(void *s1, void *s2, int c, size_t n); recopie n octets de la zone mémoire pointée par s2 dans la zone mémoire pointée par s1, mais en s'arrêtant éventuellement après que la première occurrence de l'octet c ait été copiée; retourne un pointeur sur s1 ou bien NULL si c n'a pas été trouvé dans les n octets de s2. void *memmove(void *s1, void *s2, size_t n); fonction identique à memcpy, mais la fonction garantit un comportement correct même si les deux zones se chevauchent en mémoire. void *memchr(void *s, int c, size_t n); retourne un pointeur sur la première occurrence de l'octet c dans les n octets de s, NULL si c n'a pas été trouvé. int memcmp(void *s1, void *s2, size_t n); compare les n premiers octets de s2 avec les n premiers octets de s1; retourne un résultat positif si s1 > s2, négatif si s1 < s2, et égal à zéro s'il y a égalité; les octets sont comparés en tant que unsigned char. void *memset(void *s, int c, size_t n); recopie l'octet c dans les n premiers octets de la zone mémoire pointée par s et retourne s.

Page 104: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 103

Remarque: Pour assurer la compatibilité avec l'environnement BSD (Unix Berkeley), la bibliothèque standard propose d'autres fonctions de manipulation de zones en mémoire centrale, mais qui ont un comportement légèrement différent des précédentes . Ces fonctions nécessitent alors l'inclusion du header <strings.h>. Il s'agit des fonctions suivantes: int bcmp(void *s1, void *s2, size_t n) compare les n premiers octets de la zone pointée par s1 avec les octets de la zone pointée par s2; retourne 0 en cas d'égalité et une valeur différente de 0 sinon. void bcopy(void *s1, void *s2, size_t n) recopie n octets de la zone pointée par s1 dans la zone pointée par s2. void bzero(char *s, int n) met les n octets de la zone pointée par s à zéro. int ffs(int i) retourne la position du premier bit positionné à 1 dans i en partant des bits de poids faible; le bit de poids faible est noté 1 et le bit de poids fort 32; la fonction retourne 0 s'il n'y a aucun bit à 1 dans i.

Page 105: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 104

6 Les fonctions utilitaires: La bibliothèque standard contient également un ensemble de fonctions de conversions de type, de gestion de la mémoire, et des opérations similaires. Ces fonctions nécessitent l'inclusion du header <stdlib.h>. 6-1 Les fonctions de conversion: long strtol(char *s, char **ptr, int base) retourne un long qui est la traduction de la chaîne s suivant la base de conversion indiquée par base; la chaîne est analysée sans tenir compte des espaces ou tabulations de départ; elle initialise le pointeur ptr au premier caractère non compatible avec la base; ptr pointe sur NULL si toute la chaîne a été interprétée. Si aucun caractère n'a pu être interprété ptr pointe sur s et la fonction retourne 0. Si base est compris entre 2 et 36, la conversion s'effectue en considérant que s est écrite dans cette base. Si base vaut 0 ou 16, la chaîne doit inclure le préfixe '0x' pour interpréter en base 16, ou le préfixe '0' pour une base en octal, sinon base est interprétée comme une base 10. unsigned long strtoul(char *str, char **ptr, int base) fonction identique à strtol sauf que le résultat retourné est de type unsigned long. int atoi(char *s) convertit s en un int; équivaut à strtol(s,(char**)NULL,10) . long atol(char *s) convertit s en un long; équivaut à strtol(s,(char**)NULL,10) . double strtod(char *s, char **ptr) retourne un double qui est la traduction de la chaîne s; la chaîne est analysée sans tenir compte des espaces ou tabulations de départ; elle initialise le pointeur ptr au premier caractère non interprété; ptr pointe sur NULL si toute la chaîne a été interprétée, et ptr pointe sur s si aucun caractère n'a pus être interprété. double atof(char *s) retourne un double qui est la traduction de la chaîne s; cette fonction est équivalente à strtod(s,(char**)NULL). char *ecvt(double nombre, size_t nb, int *decpt, int *signe) convertit nombre en une chaîne de nb chiffres, et retourne un pointeur sur la chaîne; la chaîne ne contient pas alors de virgule pour marquer la partie décimale et la position de la virgule par rapport au début de la chaîne est stockée indirectement dans decpt; une valeur négative de decpt signifie que la virgule est située à gauche des chiffres retournés; si le résultat est négatif, signe est positionné à une valeur non nulle, et à 0 sinon. char *fcvt(double nombre, size_t nb, int *decpt, int *signe) identique à ecvt(), sauf qu'ici nb spécifie le nombre de chiffres après la virgule.

Page 106: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 105

6-2 Les fonctions de gestion de la mémoire: void *malloc (size_t size) alloue une zone mémoire de size octets correctement alignés et retourne un pointeur sur cette zone ou bien NULL si la demande ne peut pas être satisfaite. void *calloc(size_t nb, size_t size) alloue une zone mémoire pour un tableau de nb objets de taille size, ou bien NULL si cette demande ne peut pas être satisfaite. La mémoire allouée est initialisée par des zéros. void *realloc(void *ptr, size_t size) modifie la taille initiale du bloc pointé par ptr en size octets et renvoie un pointeur sur le nouveau bloc éventuellement déplacé, ou bien NULL si cette demande ne peut pas être satisfaite; le contenu reste inchangé dans la limite de la plus petite des deux tailles. void free(void *ptr) libère le bloc mémoire pointé par ptr; l'argument ptr doit être un pointeur sur un bloc alloué auparavant par un appel à malloc, calloc ou realloc. 6-3 Les fonctions de génération de nombres pseudo-aléatoires: int rand(void) utilise la méthode des congruences avec une période de 232 pour générer un entier pseudo-aléatoire compris entre 0 et RAND_MAX défini dans le header <stdlib.h>. void srand(unsigned int seed) utilise seed pour initialiser une nouvelle séquence de nombres pseudo-aléatoires obtenue par des appels successifs à rand. double drand48(void) retourne lors des appels successifs, des valeurs à virgule flottante non négatives à double précision uniformément réparties dans l'intervalle [0.0, 1.0[. long int lrand48(void) retourne lors des appels successifs, des entiers longs non négatifs uniformément répartis dans l'intervalle [0, 231[. long int mrand48(void) retourne lors des appels successifs, des entiers longs signés uniformément répartis dans l'intervalle [-231, 231[.

Page 107: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 106

6-4 Les fonctions utilitaires: int system(char *s) permet d'exécuter la commande shell s passée en argument; si s vaut NULL la valeur de retour est une valeur non nulle, sinon la valeur retournée est le code retour de la commande invoquée. void abort(void) génère un signal de terminaison anormale du programme, ferme tous les fichiers ouverts et envoi le signal SIGABRT défini dans <signal.h> au processus père. int atexit(void (*ptfonc)(void)) indique que la fonction sans argument ptfonc devra être appelée lors de l'arrêt normal du programme, c'est à dire lors de l'appel à exit ou en retour de main; on peut enregistrer jusqu'à 32 fonctions par atexit qui sont exécutées dans l'ordre inverse de leur enregistrement; retourne une valeur nulle si l'enregistrement réussit. char *getenv(char *name) recherche dans l'environnement du processus appelant la chaîne d'environnement associée à name; retourne la chaîne associée à la valeur trouvée ou NULL si name n'existe pas; les variables d'environnement sont précisées dans environ et peuvent être visualisées par la commande shell env. void *bsearch(void *key, void *base, size_t nb, size_t size,

int (*cmp)(void *arg1, void *arg2)) recherche de l'objet *key parmi le tableau base trié par ordre croissant de nb éléments de taille size; la fonction de comparaison cmp doit retourner une valeur négative si arg1 (la clé de recherche) est plus petit que arg2 (un élément du tableau), zéro s'ils sont égaux, et une valeur positive si arg1 est plus grand que arg2; retourne un pointeur indiquant où l'objet *key est localisé dans le tableau identique, NULL s'il n'en existe aucun. void qsort (void *base, size_t nb, size_t size,

int (*cmp) (voild *, void *) ) trie suivant l'algorithme du "quick-sort" dans l'ordre croissant le tableau base comprenant nb objets de taille size; la fonction de comparaison cmp doit avoir les mêmes caractéristiques que pour bsearch. int abs (int n) retourne la valeur absolue de l'entier n. long labs (long n) retourne la valeur absolue de l'entier long n. div_t div(int num, int denum) calcule le quotient et le reste de la division du numérateur num par le dénominateur denum; retourne une structure de type div_t définie dans <stdlib.h> comprenant un champ quot et un champ rem où sont placés respectivement le quotient et le reste.

Page 108: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 107

ldiv_t ldiv( long lum, long denum) ldiv est similaire à div mais les arguments et les champs de la structure retournée sont tous de type long int; les noms des champs de la structure de type l_div sont les mêmes que pour div. 7 Les appels système: Les appels système Unix permettent au programmeur d'invoquer des services de bas niveau offerts par le noyau. En effet chaque appel implémenté est traduit par une fonction C qui assure l'interface entre le système et les programmes utilisateurs. On peut classer ces fonctions en plusieurs familles, suivant la classe de problèmes qu'elles prennent en charge:

• gestion des entrées-sorties et accès au système de gestion de fichiers • gestion des processus • gestion de la communication et de la synchronisation entre processus

Seuls sont cités les appels les plus courants. Les fichiers d'en-tête à inclure sont variables suivant l'appel système concerné, c'est pourquoi ils sont précisés ici au cas par cas. void exit(int status) termine normalement le processus appelant et permet de positionner le code retour grâce à status; ce code retour peut être testé à partir du shell par $? ou dans le processus père par l'appel système wait; il est généralement positionné à 0 pour indiquer une exécution correcte du programme; exit provoque l'exécution des fonctions enregistrées par atexit, l'écriture forcée des tampons associés aux fichiers ouverts, et la fermeture des flots ouverts; nécessite l'inclusion de <stdlib.h>. int creat (char *name, int protect) crée ou écrase le fichier de nom name; si le fichier existe déjà, il est ouvert en écriture et donc écrasé, sinon il est crée avec les droits d'accès précisés par l'argument protect; ces droits d'accès sont exprimés comme dans la commande chmod du système Unix; retourne un File Descriptor si la création s'est bien passée, sinon renvoie un entier négatif; nécessite l'inclusion de <fcntl.h>. int open(char *name, int access, int perms) ouvre le fichier de nom name dans le mode exprimé par access avec les autorisations perms, renvoie le File Descriptor associé au fichier ou -1 en cas d'erreur; l'argument access prend les valeurs définies par les constantes O_RDONLY, O_WRONLY,O_RDWR,O_TRUNC,O_APPEND et O_CREAT qui peuvent être combinées entre elles avec le ou logique |; perms est optionnel et prend les valeurs classiques des droits d'accès; nécessite l'inclusion de <fcntl.h>. int close(int fd) ferme le fichier référencé par fd; nécessite l'inclusion de <stdlib.h>.

Page 109: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 108

int unlink(int fd) détruit le fichier référencé par fd; nécessite l'inclusion de <stdlib.h>. int rename(char *anc, char * nouv) renomme le fichier de nom anc par nouv; retourne 0 si l'opération réussit, -1 sinon; nécessite l'inclusion de <stdio.h>. int read(int fd, char *buf, int nbr) lit nbr octets dans le fichier référencé par fd, et les place dans la zone mémoire désignée par buf; renvoie le nombre exact d'octets réellement lus, 0 en cas de fin de fichier, et -1 en cas d'erreur; nécessite l'inclusion de <sys/types.h> et de <unistd.h>. int write(int fd, char *buf, int nbr) écrit nbr octets dans le fichier référencé par fd depuis la zone mémoire désignée par buf; renvoie le nombre exact d'octets réellement écrits, -1 en cas d'erreur; nécessite l'inclusion de <sys/types.h> et de <unistd.h>. long lseek(int fd, long offset, int origin) permet de modifier la position courante du fichier référencé par fd, de offset octets à partir de la position indiquée par origin et dont les valeurs peuvent être: SEEK_SET début de fichier, SEEK_CUR position courante, SEEK_CUR fin du fichier; renvoie la nouvelle position dans le fichier ou -1 en cas d'erreur; l' offset doit être un entier long (ex 3L) et pour revenir en arrière, offset doit être négatif; nécessite l'inclusion de <stdlib.h>. time_t time(time_t *t) retourne l'heure calendaire actuelle exprimée en secondes depuis le 1 Janvier 1970 à 00:00:00 GMT ou bien -1 si l'heure n'est pas disponible; si t est différent de NULL, la valeur de retour est également stockée dans l'emplacement pointé par t; nécessite l'inclusion de <time.h>. clock_t times(struct tms *buf) remplit la structure de type tms définie dans <times.h>; chaque champ représente un temps exprimé en nombre de tops horloge; le champ tms_utime est le temps d'utilisation de la CPU par le processus d'appel (temps utilisateur), tms_stime est le temps CPU utilisé par le système pour le compte du processus d'appel (temps système); tms_cutime est la somme de tms_utime et de tms_cutime des processus fils; tms_cstime est la somme de tms_stime et de tms_cstime des processus fils; retourne -1 si l'opération échoue; nécessite l'inclusion de <times.h>.

Page 110: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 109

8 Les fonction de traitement du temps: La bibliothèque standard fournit des fonctions de traitement de la date et de l'heure. Ces fonctions nécessitent l'inclusion du fichier d'en-tête <time.h>. clock_t clock (void) retourne le temps d'utilisation en microsecondes de la CPU par le processus depuis le premier appel à clock; le temps totalisé est le temps utilisateur et le temps système du processus d'appel; en divisant la valeur retournée par la constante CLOCKS_PER_SEC on obtient le temps en secondes; la précision est de l'ordre de 10 ms, mais dépend de l'implémentation. char *asctime(struct tm *timeptr) convertit *timeptr qui est une structure struct tm comprennant les champs:

int tm_sec; /* secondes [0 à 61]pour les débordements */ int tm_min; /* minutes [0 à 59] */ int tm_hour; /* heures [0 à 23] */ int tm_mday; /* jour du mois [1 à 31] */ int tm_mon; /* mois [0 à 11] */ int tm_year; /* année depuis 1900 */ int tm_wday; /* jour depuis dimanche [0 à 6] */ int tm_yday; /* jour dans l'année [0 à 365] */ int tm_isdst; /* flag pour l'heure d'été */

en une chaîne de 26 caractères du type: "Wed Jun 30 21:49:08 1993\n\0" struct tm *localtime(time_t *timer) convertit l'heure calendaire pointée par timer exprimée en secondes depuis le 1 Janvier 1970 à 00:00:00 GMT en heure locale tenant compte d'une éventuelle heure d'été et retourne une structure de type tm telle que vue précédemment. char *ctime(time_t *timer) convertit l'heure calendaire pointée par timer, et retourne un pointeur sur une chaîne de 26 caractères identique à celle retournée par asctime; cette fonction est équivalente à asctime(localtime(timer)). double difftime(time_t time1, time_t time0) retourne sous forme de double, la différence exprimée en secondes entre l'heure calendaire time1 et l'heure calendaire time0. struct tm *gmtime(time_t *timer) convertit l'heure calendaire pointée par timer en heure GMT; retourne un pointeur sur une structure de type tm telle que vue précédemment. time_t mktime(struct tm *timeptr) convertit l'heure représentée par la structure tm pointée par timeptr en temps calendaire, c'est à dire en secondes écoulées depuis le 1 Janvier 1970 à 00:00:00 GMT; retourne l'heure calendaire ou -1 si l'opération a échoué.

Page 111: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 110

size_t strftime(char *s, size_t max, char *format, struct tm *timeptr) range dans le tableau sur lequel pointe s, les informations de date et d'heure contenues dans la structure tm pointée par timeptr et décodées suivant le format précisé par format; les caractères normaux y compris '\0' sont recopiés dans s; la fonction place au plus max caractères dans s et retourne la longueur de s, ou bien 0 si elle a produit plus de max caractères; les directives de la chaîne format peuvent prendre les valeurs suivantes:

%% comme % %a jour de la semaine en abrégé %A jour de la semaine en entier %b nom du mois en abrégé %B nom du mois complet %c représentation locale de la date et de l'heure. %d jour du mois (de 1 à 31) %H heure (de 00 à 23) %I heure (de 01 à 12) %j numéro du jour dans l'année (de 1 à 366) %m numéro du mois de l'année (de 1 à 12) %M minutes (de 00 à 59) %n comme \n %p équivalent local de AM (matin) ou PM(après-midi) %R heure au format %H:%M %S secondes (de 0 à 61) %t tabulation %T heure au format %H:%M:%S %w numéro du jour de la semaine (de 0 à 6): dimanche=0 %W numéro de la semaine dans l'année (de 0 à 53); tous les jours précédents le

premier lundi de la nouvelle année sont considérés comme appartenant à la semaine 0

%x représentation locale de la date %X représentation locale de l'heure %y année dans le siècle (de 0 à 99) %Y année représentée par 4 chiffres %Z nom du fuseau horaire utilisé (vide si aucun fuseau spécifié)

Page 112: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 111

9 Les fonctions mathématiques: La bibliothèque standard libc.a ne contient pas de fonctions mathématiques. Dès lors que le programmeur a besoin d'utiliser de telles fonctions, il doit mettre en ligne au moment de l'édition des liens la bibliothèque mathématique libm.a et inclure dans ces fichiers sources le fichier d'en-tête <math.h>. Les principales fonctions mathématiques fournies par cette bibliothèque sont les suivantes: double sin(double x) retourne le sinus de x exprimé en radians. float sinf(float x) même fonction que sin mais version simple précision. double cos(double x) retourne le cosinus de x exprimé en radians. float cosf(float x) même fonction que cos mais version simple précision. double tan(double x) retourne la tangente de x exprimé en radians. float tanf(float x) même fonction que tan mais version simple précision. double asin(double x) retourne l'arc sinus x en radians dans l'intervalle [-π/2, +π/2]. float asinf(float x) même fonction que asin mais version simple précision. double acos(double x) retourne l'arc cosinus de x en radians dans l'intervalle [0, +π]. float acosf(float x) même fonction que acos mais version simple précision. double atan(double x) retourne l'arc tangente de x en radians dans l'intervalle (-π/2, +π/2). float atanf(float x) même fonction que atan mais version simple précision. double atan2(double x, double y) retourne l'arc tangente de y/x en radians dans l'intervalle [-π, +π], le signe des deux arguments servant à déterminer le quadrant de la valeur retournée.

Page 113: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 112

float atan2f(float x, float y) même fonction que atan2 mais version simple précision. double sinh(double x) retourne le sinus hyperbolique de x. float sinhf(float x) même fonction que sinh mais version simple précision. double cosh(double x) retourne le cosinus hyperbolique de x. float coshf(float x) même fonction que cosh mais version simple précision. double tanh(double x) retourne la tangente hyperbolique de x. float tanhf(float x) même fonction que tanh mais version simple précision. double asinh(double x) retourne le sinus hyperbolique inverse de x. double acosh(double x) retourne le cosinus hyperbolique inverse de x. double atanh(double x) retourne la tangente hyperbolique inverse de x. double exp(double x) retourne ex. float expf(float x) même fonction que exp mais version simple précision. double log(double x) retourne le logarithme népérien de x. float logf(float x) même fonction que log mais version simple précision. double log10(double x) retourne le logarithme base 10 de x. float log10f(float x) même fonction que log10 mais version simple précision. double pow (double x, double y) retourne xy; si x<0 alors y doit être un entier et si x=0, alors y doit être positif.

Page 114: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 113

float powf(float x) même fonction que pow mais version simple précision. double sqrt(double x) retourne la racine carrée de x, avec x≥0 . float sqrtf(float x) même fonction que sqrt mais version simple précision. double floor(double x) retourne le plus grand entier inférieur à x. float floorf(float x) même fonction que floor mais version simple précision. double ceil(double x) retourne le plus petit entier non inférieur à x. float ceilf(float x) même fonction que ceil mais version simple précision. double copysign(double x, double y) retourne x avec le signe de y. double fmod(double x, double y) retourne le reste de x/y exprimé en virgule flottante avec le signe de x. float fmodf(float x, float y) même fonction que fmode mais version simple précision. double modf(double x, double *ipart) retourne la partie fractionnaire de x et place sa partie entière dans *ipart; les deux parties décimales et entières sont du même signe que x. double fabs(double x) retourne la valeur absolue du flottant x. float fabsf(float x) même fonction que fabs mais version simple précision. double rint(double x) retourne la valeur entière exprimée en virgule flottante la plus proche de x. double hypot(double x, double y) retourne la racine carrée de x2 + y2; positionne la variable errno à ERANGE si dépassement de capacité. double ldexp(double x, int n) retourne x*2n + y2.

Page 115: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 114

10 Les limites de représentation des types de base: Il existe deux fichiers d'en-tête qui fixe les limites de représentation des types de base suivant les caractéristiques de l'environnement. Il s'agit du header <limits.h> pour les entiers et caractères signés ou non, et du header <float.h> pour les réels en virgule flottante. On donne ici un extrait de ces deux fichiers d'en-tête situés sous le répertoire /usr/include. Extrait du fichier d'en-tête <limits.h>: #define CHAR_BIT 8 /* Number of bits in a char */ #define CHAR_MAX 127 /* Max integer value of a char */ #define CHAR_MIN (-128) /* Min integer value of a char */ #define INT_MAX 2147483647 /* Max decimal value of an int */ #define INT_MIN (-2147483647 - 1) /* Min decimal value of an int */ #define LONG_MAX 2147483647L /* Max decimal value of a long */ #define LONG_MIN (-2147483647L - 1) /* Min decimal value of a long */ #define SCHAR_MAX 127 /* max value of a signed char */ #define SCHAR_MIN (-128) /* Min value of a signed char */ #define SHRT_MAX 32767 /* max decimal value of a short */ #define SHRT_MIN (-32768) /* Min decimal value of a short */ #define UCHAR_MAX 255 /* max value of unsigned char */ #define UINT_MAX 4294967295 /* max value of an unsigned integer */ #define ULONG_MAX 4294967295 /* max value of a unsigned long int */ #define USHRT_MAX 65535 /* max value of a unsigned short int */ Extrait du fichier d'en-tête <float.h>: #define FLT_RADIX 2 #define FLT_ROUNDS 1 #define FLT_MANT_DIG 24 #define FLT_EPSILON ((float)1.19209290E-07) #define FLT_DIG 6 #define FLT_MIN_EXP (-125) #define FLT_MIN ((float)1.17549435E-38) #define FLT_MIN_10_EXP (-37) #define FLT_MAX_EXP 128 #define FLT_MAX ((float)3.40282347E+38) #define FLT_MAX_10_EXP 38 #define DBL_MANT_DIG 53 #define DBL_EPSILON 2.2204460492503131E-16 #define DBL_DIG 15 #define DBL_MIN_EXP (-1021) #define DBL_MIN 2.2250738585072014E-308 #define DBL_MIN_10_EXP (-307) #define DBL_MAX_EXP 1024 #define DBL_MAX 1.7976931348623157E+308 #define DBL_MAX_10_EXP 308 #define LDBL_MANT_DIG 113 #define LDBL_EPSILON 1.9259299443872358530559779425849273E-34L #define LDBL_DIG 33 #define LDBL_MIN_EXP (-16381) #define LDBL_MIN 3.3621031431120935062626778173217526026E-4932L #define LDBL_MIN_10_EXP (-4931) #define LDBL_MAX_EXP 16384 #define LDBL_MAX 1.1897314953572317650857593266280070162E4932L #define LDBL_MAX_10_EXP 4932

Page 116: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 115

Page 117: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 116

ANNEXE B - SQUELETTE D'UN PROGRAMME EN LANGAGE C

Pour la réalisation des travaux pratiques, il est vivement souhaité de rendre les listings des programmes écrits en langage C présentés de la façon suivante: /************************************************************************** Nom_etudiants : Nom_tp.c : Repertoire : Num_groupe : Date : Description : **************************************************************************/ /*--- Inclusion des headers ---------------------------------------------*/ #include <nom_header.h> #include "my_header.h" /*--- Definition des constantes -----------------------------------------*/ #define NOM_CONST valeur /* eventuellement commentaire */ /*--- Definition des macro-instructions ---------------------------------*/ #define nom_macro definition_macro /*--- Definition de types -----------------------------------------------*/ typedef type NOM_TYPE; /* eventuellement commentaire */ /*--- Definition des variables globales ---------------------------------*/ type nom_var_globale; /* eventuellement commentaire */ /*--- Prototypage des fonctions -----------------------------------------*/ type_resultat nom_fonction(liste_type_parametres); void nom_proc(liste_type_parametres); /*--- Point d'entree du programme ---------------------------------------*/ int main(void) { type nom_var_locale; /* eventuellement commentaire */ return (0 ou 1); }

Page 118: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 117

/*--- Definition des fonctions ------------------------------------------*/ type_resultat nom_fonction(liste_parametres_formels) /*========================================================================= Description : Entrees : Sortie : E/S : Exceptions : =========================================================================*/ { type nom_var_locale; /* eventuellement commentaire */ return (valeur ou expression); } void nom_proc(liste_parametres_formels) /*======================================================================== Description : Entrees : E/S : Exceptions : =========================================================================*/ { type nom_var_locale; /* eventuellement commentaire */ }

Page 119: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 118

ANNEXE C - TABLE ASCII Oct. Déc. Héxa. Caractère Oct. Déc. Héxa. Caractère000 0 00 NUL '\0' 040 32 20 SPACE 001 1 01 SOH 041 33 21 ! 002 2 02 STX 042 34 22 " 003 3 03 ETX 043 35 23 # 004 4 04 EOT 044 36 24 $ 005 5 05 ENQ 045 37 25 % 006 6 06 ACK 046 38 26 & 007 7 07 BEL '\a' 047 39 27 ' 010 8 08 BS '\b' 050 40 28 ( 011 9 09 HT '\t' 051 41 29 ) 012 10 0A LF '\n' 052 42 2A * 013 11 0B VT '\v' 053 43 2B + 014 12 0C FF '\f' 054 44 2C , 015 13 0D CR '\r' 055 45 2D - 016 14 0E SO 056 46 2E . 017 15 0F SI 057 47 2F / 020 16 10 DLE 060 48 30 0 021 17 11 DC1 061 49 31 1 022 18 12 DC2 062 50 32 2 023 19 13 DC3 063 51 33 3 024 20 14 DC4 064 52 34 4 025 21 15 NAK 065 53 35 5 026 22 16 SYN 066 54 36 6 027 23 17 ETB 067 55 37 7 030 24 18 CAN 070 56 38 8 031 25 19 EM 071 57 39 9 032 26 1A SUB 072 58 3A : 033 27 1B ESC 073 59 3B ; 034 28 1C FS 074 60 3C < 035 29 1D GS 075 61 3D = 036 30 1E RS 076 62 3E > 037 31 1F US 077 63 3F ?

Page 120: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 119

Oct. Déc. Héxa. Caractère Oct. Déc. Héxa. Caractère100 64 40 @ 140 96 60 ` 101 65 41 A 141 97 61 a 102 66 42 B 142 98 62 b 103 67 43 C 143 99 63 c 104 68 44 D 144 100 64 d 105 69 45 E 145 101 65 e 106 70 46 F 146 102 66 f 107 71 47 G 147 103 67 g 110 72 48 H 150 104 68 h 111 73 49 I 151 105 69 i 112 74 4A J 152 106 6A j 113 75 4B K 153 107 6B k 114 76 4C L 154 108 6C l 115 77 4D M 155 109 6D m 116 78 4E N 156 110 6E n 117 79 4F O 157 111 6F o 120 80 50 P 160 112 70 p 121 81 51 Q 161 113 71 q 122 82 52 R 162 114 72 r 123 83 53 S 163 115 73 s 124 84 54 T 164 116 74 t 125 85 55 U 165 117 75 u 126 86 56 V 166 118 76 v 127 87 57 W 167 119 77 w 130 88 58 X 170 120 78 x 131 89 59 Y 171 121 79 y 132 90 5A Z 172 122 7A z 133 91 5B [ 173 123 7B { 134 92 5C \ '\\' 174 124 7C | 135 93 5D ] 175 125 7D } 136 94 5E ^ 176 126 7E ~ 137 95 5F _ 177 127 7F DEL

Page 121: du C-ANSI au C++ du Cdu C--ANSI au C++ANSI au C++olazo.free.fr/IUT/Cours/nouveau/Le poly de langage C.pdf · Université LYON I ~ IUT-A Département Informatique ~ A.P. ~ Programmation

Université LYON I ~ IUT-A Département Informatique ~ Programmation procédurale: du C-ANSI au C++ 120

ANNEXE D - BIBLIOGRAPHIE SUCCINCTE LE LANGAGE C, Norme ANSI - 2000 par B.W. KERNIGHAN et D.M. RITCHIE Editions DUNOD EXERCICES CORRIGES SUR LE LANGAGE C Solutions des exercices du K & R - 2000 par C.L. TONDO et S.E. GIMPEL Editions DUNOD METHODOLOGIE DE LA PROGRAMMATION EN LANGAGE C - 2000 par J.-P. BRAQUELAIRE Editions DUNOD LE LANGAGE C - 2000 par C. DELANNOY Editions EYROLLES LA PROGRAMMATION SOUS UNIX - 1989 par J.-M. RIFFLET Editions Mc GRAW-HILL UNIX: Les mécanismes internes - 1997 par J. PHILIPP Presses de l'Ecole des Ponts et Chaussées UNIX SVR4

• Manuel de référence de l'administrateur • Manuel de référence du programmeur • Guide de programmation ANSI-C Editions PRENTICE HALL / MASSON