Cours langage C

Formation Informatiques Introduction au langage C


Télécharger Formation Informatiques Introduction au langage C

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


UPMC                                                                     Master P&A/SDUEE

UE MP050

                                                  Methodes Num´   eriques et Informatiques - A´

LangageC

2012–2013                                                                          Albert Hertzog

Table des matieres`

1      Introduction        20

1.1      Programmation en langage compile´         . . . . . . . . . . . . . . . . .  20

1.2      Historique du C         . . . . . . . . . . . . . . . . . . . . . . . . . . .         22

1.3      Inter´ ets du Cˆ . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     23

1.4      Gen´         eralit´        es´    . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     24

1.5      Exemple de programme C avec une seule fonction   . . . . . . . . . .        25

1.6      Structure gen´  erale d’un programme C´  . . . . . . . . . . . . . . . .     26

1.7      Exemple de programme C avec deux fonctions . . . . . . . . . . . .    28

1.8      Compilation      . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     30

1.9      Compilateur     . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     33

 

2      Types des variables     34

2.1      Types de base  . . . . . . . . . . . . . . . . . . . . . . . . . . . .       34

2.2      Declaration et affectation des variables´  . . . . . . . . . . . . . . . .     35

2.2.1    Syntaxe et exemples        . . . . . . . . . . . . . . . . . . . . .   36

2.2.2    Valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . .         37

2.3      Domaine et representation machine des entiers´      . . . . . . . . . . . .    38

2.3.1    Domaine des entiers non-signes et sign´ es´    . . . . . . . . . . .       38

2.3.2    Representation machine des entiers´      . . . . . . . . . . . . . .       40

2.3.3    Structure simplifiee de la RAM : d´  eclaration de deux entiers´  . .      41

2.3.4    Structure simplifiee de la RAM : affectation de deux entiers´  . .      43

2.4      Valeurs maximales des entiers en C  . . . . . . . . . . . . . . . . .  44

2.4.1    Valeurs maximales des entiers en C : machine 32 bits . . . . .       45

2.4.2    Valeurs maximales des entiers en C : machine 64 bits . . . . .       46

 

2.5      Domaine et precision des flottants´ . . . . . . . . . . . . . . . . . . 47

2.5.1    Codage des flottants       . . . . . . . . . . . . . . . . . . . . .   47

2.5.2    Domaine et precision des flottants´         . . . . . . . . . . . . . . .       48

2.6      Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .         49

2.6.1    Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . .        49

2.6.2    Exemples d’attribut const en C      . . . . . . . . . . . . . . .       50

2.6.3    Exemples d’utilisation de #define . . . . . . . . . . . . . 50

3      Operateurs´         52

3.1      Operateur d’affectation´  . . . . . . . . . . . . . . . . . . . . . . . .      52

3.2      Operateurs alg´         ebriques´  . . . . . . . . . . . . . . . . . . . . . . .         54

3.3      Operateurs de comparaison´    . . . . . . . . . . . . . . . . . . . . .   55

3.4      Operateurs logiques´        . . . . . . . . . . . . . . . . . . . . . . . . .    55

 

3.5      Incrementation et d´         ecr´  ementation en C´      . . . . . . . . . . . . . . . .         56

3.6      Operateurs d’affectation compos´    ee en C´    . . . . . . . . . . . . . . .       57

3.7      Operateur d’alternative en C´   . . . . . . . . . . . . . . . . . . . . .   57

3.8      Operateurs agissant sur les bits´       . . . . . . . . . . . . . . . . . . .        58

3.9      Operateur sizeof en C´      . . . . . . . . . . . . . . . . . . . . . . . .      59

3.10   Operateur s´     equentiel´ , en C         . . . . . . . . . . . . . . . . . . . . .   59

3.11   Operateurs´      & et * en C       . . . . . . . . . . . . . . . . . . . . . . .         59

3.12   Priorites des op´       erateurs en C´   . . . . . . . . . . . . . . . . . . . . .   60

4      Entrees et sorties standard´         el´ ementaires´ 61

4.1      Gen´         eralit´        es´    . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     61

4.2      Formats d’affichage en C . . . . . . . . . . . . . . . . . . . . . . .         63

4.3      Gabarits d’affichage en C . . . . . . . . . . . . . . . . . . . . . . 65

 

4.3.1    Cas des entiers . . . . . . . . . . . . . . . . . . . . . . . .    65

4.3.2    Cas des flottants     . . . . . . . . . . . . . . . . . . . . . . .         65

5      Structures de controleˆ       66

5.1      Structure if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

5.1.1    Exemple de if . . . . . . . . . . . . . . . . . . . . . . . .      71

5.2      Structure switch . . . . . . . . . . . . . . . . . . . . . . . . .        73

5.2.1    Exemples de switch-case . . . . . . . . . . . . . . . .         74

5.3      Structures iteratives ou boucles´       . . . . . . . . . . . . . . . . . . . .      78

5.3.1    Boucle definie (´      for)  . . . . . . . . . . . . . . . . . . . . .   78

5.3.2    Exemple de boucle for . . . . . . . . . . . . . . . . . . .          80

5.3.3    Boucle indefinie (´  while et do while) . . . . . . . . .      81

5.3.4    Exemple de boucle while . . . . . . . . . . . . . . . . . .      83

5.4      Branchements . . . . . . . . . . . . . . . . . . . . . . . . . . . .       84

5.4.1    Exemple de continue . . . . . . . . . . . . . . . . . . . 85

5.4.2    Exemple de break . . . . . . . . . . . . . . . . . . . . .   86

6      Introduction aux pointeurs 87

6.1      Inter´ et des pointeursˆ    . . . . . . . . . . . . . . . . . . . . . . . . .    87

6.2      Declaration et affectation´        . . . . . . . . . . . . . . . . . . . . . . .         88

6.2.1    Declaration d’une variable ordinaire (rappel)´ . . . . . . . . . .       88

6.2.2    Affectation d’une variable ordinaire (rappel)  . . . . . . . . . .       89

6.2.3    Declaration d’un pointeur´      . . . . . . . . . . . . . . . . . . .        90

6.2.4    Affectation d’un pointeur . . . . . . . . . . . . . . . . . . . .   93

6.3      Indirection (operateur´     ?)      . . . . . . . . . . . . . . . . . . . . . . .         98

6.4      Initialisation des pointeurs et dissociation . . . . . . . . . . . . . . . 101

6.5      Bilan concernant la syntaxe des pointeurs         . . . . . . . . . . . . . . 103

6.6      Tailles des types de base et adresses en C . . . . . . . . . . . . . . 104

7      Fonctions en C    106

7.1      Gen´         eralit´        es´    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

7.2      Definition d’une fonction´         . . . . . . . . . . . . . . . . . . . . . . . 107

7.2.1    Exemples de fonctions renvoyant une valeur . . . . . . . . . . 109

7.2.2    Exemple d’une fonction sans retour        . . . . . . . . . . . . . . 111

7.2.3    Exemple d’une fonction sans argument  . . . . . . . . . . . . 112

7.3      Appel d’une fonction        . . . . . . . . . . . . . . . . . . . . . . . . . 113

7.3.1    Appel d’une fonction avec retour . . . . . . . . . . . . . . . . 113

7.3.2    Appel d’une fonction sans retour    . . . . . . . . . . . . . . . 113

7.3.3    Appel d’une fonction sans argument . . . . . . . . . . . . . . 114

7.4      Declaration d’une fonction´      . . . . . . . . . . . . . . . . . . . . . . 115

7.4.1    Declaration au moyen d’un prototype (m´       ethode conseill´ ee)´  . . . 116

7.4.2    Exemple de declaration et d’appel d’une fonction´ . . . . . . . 117

7.4.3    Declaration au moyen de la d´         efinition (m´      ethode d´   econseill´  ee)´  . 118

7.5      Quelques fonctions (standards) du C         . . . . . . . . . . . . . . . . . 119

7.5.1    La fonction principale : main . . . . . . . . . . . . . . . . . 119

7.5.2    La fonction exit . . . . . . . . . . . . . . . . . . . . . . . 120

7.5.3    Les fonctions printf et scanf . . . . . . . . . . . . . . 121

7.5.4    Les fonctions mathematiques (´      pow, fabs, ) . . . . . . . . 122

7.5.5    Exemple d’un programme utilisant math.h . . . . . . . . . 124

7.5.6    Liste des fonctions mathematiques standards´        . . . . . . . . . 126

7.6      La portee des variables´   . . . . . . . . . . . . . . . . . . . . . . . 128

7.6.1    Variables globales   . . . . . . . . . . . . . . . . . . . . . . 129

7.6.2    Variables locales     . . . . . . . . . . . . . . . . . . . . . . . 129

7.6.3    Exemple d’un programme utilisant une variable globale . . . . 130

7.7      Passage de parametres dans une fonction`       . . . . . . . . . . . . . . 132

7.7.1    Exemple de passage par valeur : le faux echange´   . . . . . . . 133

7.7.2    Le faux echange : visualisation de la RAM´       a l’ex`       ecution´    . . . . 136

7.7.3    Exemple de passage par adresse : le vrai echange´ . . . . . . 139

7.7.4    Le vrai echange : visualisation de la RAM´        a l’ex`       ecution´    . . . . 142

7.7.5    Bilan sur le passage de parametres`         . . . . . . . . . . . . . . 145

7.8      Retour sur printf/scanf     . . . . . . . . . . . . . . . . . . . . . . . . 146

7.9      Complement sur les fonctions : la r´ ecursivit´  e´      . . . . . . . . . . . . . 147 7.9.1         Exemple de fonction recursive : factorielle´       . . . . . . . . . . . 147

8      Tableaux     148

8.1      Definition et usage´ . . . . . . . . . . . . . . . . . . . . . . . . . . 148

8.1.1    Exemples de programmes el´ ementaires utilisant des tableaux 1D´   149

8.1.2    Exemples de programmes el´ ementaires utilisant des tableaux 2D´   153

8.2      Tableaux de taille fixe . . . . . . . . . . . . . . . . . . . . . . . . . 157

8.2.1    Declaration d’un tableau de taille fixe´    . . . . . . . . . . . . . 157

8.2.2    Indexation et ref´ erence´        a un` el´ ement d’un tableau´       . . . . . . . 159

8.2.3    Declaration d’un tableau 1D : visualisation de la RAM´    . . . . . 161

8.2.4    Affectation d’un tableau . . . . . . . . . . . . . . . . . . . . 162

8.2.5    Affectation d’un tableau 1D : visualisation de la RAM . . . . . . 164

8.2.6    Affectation d’un tableau 2D : visualisation de la RAM . . . . . . 165

8.2.7    Ordre des el´ ements de tableaux 2D en C´      . . . . . . . . . . . 166

8.3      Inconvenients des tableaux de taille fixe´ . . . . . . . . . . . . . . . 167

8.3.1    Directive preprocesseur (ancienne m´     ethode)´   . . . . . . . . . 168

8.3.2    Declaration tardive et tableau automatique (m´      ethode conseill´ ee)´  171

8.3.3    Exemple de programme utilisant un tableau automatique       . . . 172

8.4      Tableaux et pointeurs en C . . . . . . . . . . . . . . . . . . . . . . 174

8.4.1    Notions de base      . . . . . . . . . . . . . . . . . . . . . . . 174

8.4.2    Arithmetique des pointeurs´   . . . . . . . . . . . . . . . . . . 175

8.4.3    Retour sur l’ordre des el´ ements de tableaux 2D en C´   . . . . . 176

8.4.4    Sous-tableau 1D avec un pointeur . . . . . . . . . . . . . . . 179

8.4.5    Tableaux 2D et pointeurs         . . . . . . . . . . . . . . . . . . . 182

8.4.6    Utilisation de typedef . . . . . . . . . . . . . . . . . . . 185

8.5      Fonctions et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . 186

8.5.1    Passage de tableaux de taille fixe (pour le compilateur)  . . . . 186

8.5.2    Exemple de passage d’un tableau 1D de taille fixe . . . . . . . 187

8.5.3    Exemple de passage d’un tableau 2D de taille fixe . . . . . . . 189

8.5.4    Passage de tableau de taille inconnue a la compilation`  . . . . . 191

8.5.5    Exemple de passage d’un tableau 1D de taille variable . . . . . 192

8.5.6    Exemple de passage d’un tableau 2D de taille variable . . . . . 194

8.5.7    Limite des tableaux automatiques . . . . . . . . . . . . . . . 196

9      Allocation dynamique (sur le tas)        201

9.1      Motivation       . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

9.2      Allocation dynamique avec malloc ou calloc . . . . . . . . . . 202

9.3      Liberation de la m´   emoire allou´    ee avec´    free . . . . . . . . . . . . . 204

9.4      Attention aux fuites de memoire´     . . . . . . . . . . . . . . . . . . . 205

9.5      Exemple d’allocation dynamique d’un tableau 1D     . . . . . . . . . . . 206

9.6      Exemple d’allocation dynamique d’un tableau 2D     . . . . . . . . . . . 208

9.7      La bibliotheque` libmnitab . . . . . . . . . . . . . . . . . . . . 212

9.7.1    Exemple de passage d’un tableau dynamique 1D    . . . . . . . 213

9.7.2    Exemple de passage d’un tableau dynamique 2D    . . . . . . . 215

9.8      Bilan sur l’allocation de memoire´     . . . . . . . . . . . . . . . . . . . 217

10   Chaˆ?nes de caracteres`      218

10.1   Definition et usage´ . . . . . . . . . . . . . . . . . . . . . . . . . . 218

10.2   Exemple de tableaux de caracteres de taille fixe`      . . . . . . . . . . . 219

10.3   Exemple de tableaux de caracteres de taille quelconque` . . . . . . . 222

10.4   Declaration et affectation des cha´   ˆ?nes de caracteres`  . . . . . . . . . 225

10.4.1     Chaˆ?ne de longueur fixe . . . . . . . . . . . . . . . . . . . . 225

10.4.2     Chaˆ?ne de longueur calculee´         a l’initialisation`         . . . . . . . . . . 225

10.5   Manipulation des chaˆ?nes de caracteres`         . . . . . . . . . . . . . . . 226

10.5.1     Longueur d’une chaˆ?ne avec strlen . . . . . . . . . . . . 226

10.5.2     Concatenation de cha´    ˆ?nes avec strcat . . . . . . . . . . . 228

10.5.3     Copie d’une chaˆ?ne avec strcpy . . . . . . . . . . . . . . 230

10.5.4     Comparaison de chaˆ?nes avec strcmp . . . . . . . . . . . 231

10.5.5     Recherche d’un caractere dans une cha` ˆ?ne avec strchr . . 232 10.5.6 Recherche d’une sous-chaˆ?ne dans une chaˆ?ne avec strstr 233

11   Entrees–sorties´ 234

11.1   Introduction     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

11.1.1     Rappel : les fonctions printf et scanf . . . . . . . . . . 235

11.1.2     Exemple introductif         . . . . . . . . . . . . . . . . . . . . . . 236

11.2   Type de fichiers et acces` . . . . . . . . . . . . . . . . . . . . . . . 238

11.3   Ouverture et fermeture d’un fichier . . . . . . . . . . . . . . . . . . 240

11.3.1     Declaration d’un flux´      . . . . . . . . . . . . . . . . . . . . . 240

11.3.2     Ouverture d’un flux avec fopen . . . . . . . . . . . . . . . 241

11.3.3     Fermeture d’un flux avec fclose . . . . . . . . . . . . . . 242

11.3.4     Exemple d’ouverture/fermeture d’un fichier   . . . . . . . . . . 242

11.4   Entree-sorties format´      ees´  . . . . . . . . . . . . . . . . . . . . . . . 243

11.4.1     Ecriture avec fprintf . . . . . . . . . . . . . . . . . . . 243

11.4.2     Lecture avec fscanf . . . . . . . . . . . . . . . . . . . . 244

11.4.3     Bilan sur les entrees-sorties format´        ees´  . . . . . . . . . . . . 245

11.5   Entrees-sorties non format´      ees (binaires)´   . . . . . . . . . . . . . . . 246

11.5.1     Lecture avec fread . . . . . . . . . . . . . . . . . . . . . 246

11.5.2     Ecriture avec´ fwrite . . . . . . . . . . . . . . . . . . . . 247

11.6   Retour sur les formats d’entree–sortie´    . . . . . . . . . . . . . . . . 248

11.7   Exemple de lecture de fichier formate en C´     . . . . . . . . . . . . . . 250

11.8   Fonctions supplementaires´      . . . . . . . . . . . . . . . . . . . . . . 254 12 Structures ou types deriv´        es´    256

           12.1 Inter´ et des structuresˆ                    . . . . . . . . . . . . . . . . . . . . . . . . . 256

                                  12.1.1 Exemple introductif de structures       . . . . . . . . . . . . . . . 257

            12.2 Definition, d´  eclaration et initialisation des structures´   . . . . . . . . . . 260

12.2.1     Definition d’un type structure´        point . . . . . . . . . . . . . 260

12.2.2     Quels types de champs peut-on mettre dans une structure ? . . 260

12.2.3     Ou placer la d`         efinition d’une structure ?´        . . . . . . . . . . . . 261

12.2.4     Declaration de variables de type structure´     point . . . . . . 262

12.2.5     Affectation d’une structure (constructeur)      . . . . . . . . . . . 262

               12.3 Manipulation des structures                   . . . . . . . . . . . . . . . . . . . . . 263

12.3.1     Acces aux champs d’une structure` . . . . . . . . . . . . . . 263

12.3.2     Affectation globale (meme type)ˆ   . . . . . . . . . . . . . . . . 264

12.3.3     Entrees/sorties et structures´ . . . . . . . . . . . . . . . . . . 264

12.3.4     Retour sur l’exemple introductif     . . . . . . . . . . . . . . . . 265

12.4   Representation en m´ emoire des structures´      . . . . . . . . . . . . . . 268

12.5   Pointeur sur une structure . . . . . . . . . . . . . . . . . . . . . . 269

12.6   Exemples de structures (plus ou moins) complexes . . . . . . . . . . 270

12.6.1     Tableaux de structures   . . . . . . . . . . . . . . . . . . . . 270

12.6.2     Structures contenant un tableau (taille fixe)    . . . . . . . . . . 270

12.6.3     Structures contenant un tableau (taille variable)     . . . . . . . . 271

12.6.4     Structures contenant une structure         . . . . . . . . . . . . . . 273

12.6.5     Listes chaˆ?nees´     . . . . . . . . . . . . . . . . . . . . . . . . 274

              12.7 Structure et fonction, operateur fl´ eche`       . . . . . . . . . . . . . . . . 275

12.7.1     Passage par copie de valeur    . . . . . . . . . . . . . . . . . 275

12.7.2     Passage par copie d’adresse   . . . . . . . . . . . . . . . . . 276

12.7.3     Operateur fl´  eche`         . . . . . . . . . . . . . . . . . . . . . . . 277

12.8   Valeur de retour . . . . . . . . . . . . . . . . . . . . . . . . . . . 278

12.9   Exemple final      . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279

12.10Bilan sur les structures . . . . . . . . . . . . . . . . . . . . . . . . 282

13   El´ ements de compilation s´       epar´         ee´   283

13.1   Introduction       . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

13.2   Fichiers d’entete (ˆ     header files)      . . . . . . . . . . . . . . . . . . . . 285

13.2.1     Definition et usage´         . . . . . . . . . . . . . . . . . . . . . . 285

13.2.2     Structure d’un fichier d’enteteˆ       . . . . . . . . . . . . . . . . . 287

13.3   Exemple de programme en plusieurs fichiers      . . . . . . . . . . . . . 288

13.4   Bibliotheques statiques de fichiers objets`  . . . . . . . . . . . . . . . 291

13.4.1     Creation et utilisation d’une biblioth´      eque statique (archive)`         . . . 291

13.4.2     Retour sur la bibliotheque standard`       . . . . . . . . . . . . . . 294

13.4.3     Retour sur la bibliotheque` libmnitab . . . . . . . . . . . 295

13.4.4     Bilan sur la creation et l’usage d’une biblioth´ eque`         . . . . . . . 296

13.5   Gen´   eration d’un fichier ex´      ecutable avec´  make . . . . . . . . . . . . 297

13.5.1     Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

13.5.2     Construction d’un makefile . . . . . . . . . . . . . . . . 298

13.5.3     Exemple el´ ementaire de´      makefile en C . . . . . . . . . . 299

13.5.4     Utilisation d’un makefile . . . . . . . . . . . . . . . . . 301

14   Conclusions         302


                                        1      Introduction

                                        1.1         Programmation en langage compile´

Conception, ecriture et ex´    ecution d’instructions destin´     ees´  a` etre traitˆ      ees de mani´     ere` automatique par un appareil informatique :

–   conception : definir l’objectif du programme et la m´ ethode´     a utiliser` ? algorithmique

–   codage : ecrire le programme suivant la syntaxe d’un langage de haut niveau,´ portable et utilisant des bibliotheques : C, fortran, `

? code source : fichier texte avec instructions commentees compr´ ehensibles´ pour le concepteur et les autres

–   compilation : transformer le code source en un code machine

? code objet puis code executable : fichiers binaires compr´      ehensibles par la´ machine (le processeur)

–   execution´         : tester le bon fonctionnement du programme

     ? exploitation des capacites de l’appareil informatique et production de r´                                                                                              esultats´

1.1 Programmation en langage compile´

–   L’ordinateur est muni d’un systeme d’exploitation` (exemple : linux).

–   Le code source est un fichier texte ecrit au moyen d’un´ editeur de texte´      .

Exemples : vi, emacs, kate, kwrite, sous linux. Un fichier code source C doit avoir une extension .c

–   Le code machine (fichier objet ou executable) est g´ en´ er´ e par un´      compilateur a : programme qui analyse le code source, signale les erreurs de syntaxe, produit des avertissements sur les constructions suspectes, convertit un code source en code machine, optimise le code machine

Exemples :

gcc (GNU Compiler Collection - compilateur C standard sous UNIX et linux), icc (compilateur C d’Intel).

–   Les instructions du programme sont execut´      ees par un´ processeur caracteris´       e´ par son architecture, la taille de ses registres (nombre de bits traites ensemble :´ 32, 64 bits), sa vitesse d’horloge (mega ou giga Hertz), son jeu d’instructions

Exemples : famille x86 d’Intel (32 bits), Pentium (32/64 bits) ou x64 (64 bits)

 

                                                     a. Il existe des langages interpret´ es qui ex´      ecutent les instructions sans compilation (shell scripts).´

1.2 Historique du C

                                        1.2       Historique du C

–   langage conc¸u dans les annees 1970´

–   1978 : parution de The C Programing Langage de B. KERNIGHAN et D. RICHIE

–   developpement li´     e´ a la diffusion du syst`    eme` UNIX

–   1988–90 : normalisation C89 ANSI–ISO (bibliotheque standard du C)`

                                                Deuxieme`  edition du K´       ERNIGHAN et RICHIE norme ANSI

–   1999 : norme C99 en cours d’implementation :´

Ajout de nouveaux types (booleen, complexe, entiers de diverses tailles (prise en´ compte des processeurs 64 bits), caracteres`        etendus (unicode), ).´ Introduction de la gen´       ericit´ e dans les fonctions num´ eriques,´

declarations tardives des variables, tableaux automatiques de taille variable ´

                                                  ? se conformer a une`   norme pour la portabilite´

–   base d’autres langages dont C++ (premier standard en 1998), PHP, Java, etc.

1.3 Inter´ ets du Cˆ

                                        1.3    Inter´ ets du Cˆ

Langage C

langage de haut niveau

              (structures de controle, structures de donnˆ   ees, fonctions, compilation s´ epar´ ee, )´

mais aussi langage de bas niveau

(manipulation de bits, d’adresses, )

applications scientifiques et de gestion

                                                     langage portable graceˆ   a la norme et`  a des biblioth` eques`

langage puissant, efficace, mais aussi permissif !

                                                                                          ecriture de syst´ emes d’exploitation`

                                                                                                                                                                                                                                     1.4 Gen´                                                                                                                                                                       eralit´                                                                                                                                                                            es´

                                        1.4    Gen´ eralit´ es´

 

langage C

format

libre

? mettre en evidence les structures par la mise en page´

(indentation)

ligne

pas une entite particuli´  ere (sauf pr` eprocesseur)´

(la fin de ligne est un separateur comme l’espace, la tabulation, )´

fin d’instructions

instructions simples terminees par´      ;

commentaire

entre /* et */ (pas d’imbrication selon la norme) en C99 et C++ : introduit par // et termine en fin de ligne´

directive preprocesseur´

Introduite par # en premiere colonne`

Identificateur variable

commence par une lettre ou       .

31 caracteres alphanum`   eriques plus´     sont distingues´

maj/minuscule

distinction

1.5 Exemple de programme C avec une seule fonction

                                        1.5               Exemple de programme C avec une seule fonction

/* programme elem0.c */

#include <stdio.h>

 

/* instructions preprocesseur´                          */

#include <stdlib.h>

*/

 

/* = directives

int main(void)

 

/* fonction principale */

{

 

/* <<= debut du bloc principal */

int i ;

 

/* declaration´            */

int s=0 ;

 

/* declaration´                           + initialisation*/

for (i = 1 ; i <= 5 ;

i++)

/* structure de boucle */

{

s += i ;

 

/* <<= debut´              de sous bloc */

}

 

/* <<= fin de sous bloc */

printf("somme des entiers de 1 à 5\n") ; printf("somme = %d\n", s) ;

                   exit(0) ;                                                                           /* renvoie a` unix le status 0 (OK) */

}                                                                                          /* <<= fin du bloc principal */

                                                                                                                                                                                                                                     1.6 Structure gen´                                                                                                                 erale d’un programme C´

                                        1.6        Structure gen´ erale d’un programme C´

                                        Structure gen´  erale d’un programme C´ el´ ementaire :´

/* programme elem1.c */

#include <stdio.h> #include <stdlib.h>

int main(void)

{

Déclarations des variables;

Instructions exécutables;

}

Conseils pour la mise en page :

–   une instruction par ligne

–   indenter a l’ouverture d’un bloc`

                                                                                                                                                                                                                                     1.6 Structure gen´                                                                                                                 erale d’un programme C´

–   Une instruction simple doit se terminer par ;

–   Une instruction composee´       est constituee d’un´ bloc d’instructions (imbrication des blocs possible). Elle est delimit´ ee par des accolades´        { et }.

–   Un programme C = une (ou plusieurs) fonction(s) dont au moins la fonction main : le programme principal.

N.-B : a l’ext`      erieur de ces fonctions, il peut comporter des instructions, des´ declarations de variables, des d´         eclarations de fonctions sp´ ecifiant leur prototype,´ et des directives pour le preprocesseur introduites par´  #.

–   La definition d’une´   fonction se compose d’un enteteˆ     et d’un corps (entre { et }) qui est en fait une instruction composee.´

–   L’enteteˆ  d’une fonction specifie le´ type de la valeur de retour, le nom de la fonction et ses parametres ou arguments :`

type nom_fonction (type1 arg1, type2 arg2, )

                                                  ou chaque argument est d` eclar´ e par son type, suivi de son identificateur´

1.7 Exemple de programme C avec deux fonctions

                                        1.7     Exemple de programme C avec deux fonctions

/* programme elem.c */

/* debut des instructions preprocesseur´                                */

#include <stdio.h>                                                                     /* pour les entrees/sorties´                                                                                       */

#include <stdlib.h>                                       /* par exemple pour exit */

/* fin des instructions preprocesseur´                                 */

int somme(const int p) ;       /* declaration´      de la fonction somme */ int main(void)   /* fonction principale (sans param.)*/

{                                                                                    /* <<= debut´      de bloc */

                  int s ;                                                                   /* declaration´   de l’entier s */

printf("somme des entiers de 1 à 5\n");/* avec retour ligne => "\n" */

s = somme(5) /* appel de la fonction somme */ printf("somme = %d\n", s) ; /* impression du resultat´    */

                   exit(0) ;                                 /* renvoie a` unix un status 0 (OK) */

}                                                                                   /* <<= fin de bloc */

/*                                                                                                               */

1.7 Exemple de programme C avec deux fonctions

int somme(const int p)                                               /* definition´  de la fonction somme */

/*

* calcul de la somme des p premiers entiers

*/

{                                                                                    /* <<= debut´      de bloc */

                       int i , sum ;                                                       /* declaration´  des var. locales */

                                               for (i = 0, sum = 0 ; i <= p ; i++)  /* structure de boucle */

                     {                                                                      /*               <<= debut´   de bloc */

                                      sum += i ;                                                   /* sum = sum + i */

printf(" i = %d, somme partielle = %d\n", i, sum) ;

                     }                                                                      /*    <<= fin de bloc */

                  return sum ;                             /* valeur rendue par la fonction */

}                                                                                   /* <<= fin de bloc */


1.8       Compilation

–    Fichier ou code source (texte) de suffixe .c en C

–    Fichier objet (binaire) de suffixe .o

–    Fichier executable´       (binaire) a.out par defaut´

La commande de compilation gcc toto.c lance par defaut trois actions :´

1.    traitement par le preprocesseur´        (cpp) des lignes commenc¸ant par #

(transformation textuelle) ? fichier texte modifie´

2.    compilation a proprement parler`      ? fichier objet .o

3.    edition de lien´ (gcc lance ld) ? fichier executable´         a.out assemblage des codes objets et resolution des appels aux biblioth´ eques`

N.-B. : seul le fichier source est portable (independant de la machine)´

 

Options de compilation permettant de choisir les etapes et les fichiers :´

? gcc-Etoto.c : preprocesseur seulement´

? -c : preprocesseur et compilation seulement´

? -o toto.x : permet de specifier le nom du fichier ex´                 ecutable´

? -ltruc donne a` ld l’acces`              a la`  bibliotheque`        libtruc.a

(ex. : -lm pour libm.a, bibliotheque math`  ematique indispensable en C)´ Options de compilation utiles a la`        mise au point :

? verification des standards du langage (errors)´

? avertissements (warnings) sur les instructions suspectes (variables non utilisees, instructions apparemment inutiles, changement de type, )´

? verification des passages de param´       etres`

         (necessite un contr´ ole interprocˆ edural, donc les prototypes)´

? faire du compilateur un assistant efficace pour anticiper les problemes` avant l’edition de lien ou, pire, l’ex´  ecution.´


1.9 Compilateur

1.9       Compilateur

langage C

gcc (dans le shell)

avec options sev´ eres`

C89 (ANSI) ? alias gcc-mni-c89

C99? alias gcc-mni-c99

doc :

1.9 Compilateur

Type

Denomination´

   

vide

void

booleen´

(C99)                             bool

#include <stdbool.h>

 

Entier

caractere`

char

caractere large`

(C99) wchar_t

court

short (int)

courant

int

long

long (int)

plus long

(C99) long long

2     Types  des variables

Reel´

simple precision´

float

courant

double

quadruple precision´

long double

Le C est un langage type :´ il faut declarer chaque variable utilis´         ee´ dans le code ? indiquer quel est le type de la variable utilisee.´

2.1       Types de base

 

(C99)Complexe

simple

 

float complex

courant

 

(double) complex

long

 

long double complex

   

#include <tgmath.h>

2.2         Declaration et affectation des variables´

Declarer´     une variable = reserver une zone en m´   emoire pour la stocker´

Chaque variable declar´    ee est stock´  ee dans une zone m´      emoire qui lui est propre´

(memoire vive ou´      RAM) sous un certain codage :

– emplacement de cette zone : adresse unique propre a chaque variable.` – taille de cette zone : depend du´ type de la variable (et du processeur 32/64 bits).

C89 : declarer en t´   ete des fonctions (ˆ    syntaxe conseillee pour les d´                                               ebutants´                                        )

C99 : n’importe ou (` mais en tete de bloc pour la lisibilitˆ  e du code source´                                                                                                                                     )

Affecter une variable = stocker une valeur dans la zone memoire r´      eserv´                                                                                                                                      ee´

Initialiser une variable = affecter une valeur a une variable au moment de la` reservation de la m´         emoire´

Remarque importante : avant initialisation (ou premiere affectation) la valeur d’une` variable est indetermin´       ee.´


2.2.1        Syntaxe et exemples

type identifiant1, identifiant2=valeur ;

Exemples avec des entiers :

–    Declaration de 3 entiers :´     int j, j2, k_max ;

–    Declaration avec initialisation :´    int a = 2, b = 3 ;

–    Affectation (apres d`     eclaration) :´     k_max=4 ;

Exemples avec des floats :

–    Declaration de 3 floats :´        float x, y, z ;

–    Declaration avec initialisation :´    float v = 1.5f, w = 3.f ;

–    Affectation (apres d`     eclaration) :´     z=4.5f ;

Exemples avec des doubles :

–    Declaration de 3 doubles :´   double x, y, z ;

–    Declaration avec initialisation :´    double v = 1.5, w = 3. ;

–    Affectation (apres d`     eclaration) :´     z=4.5 ;

2.2.2      Valeurs

Type

langage C

bool

(C99) true

false

char

a

 

Chaˆ?nes

"chaine"

"s’il"

"\n"

short

17

 

int (decimal)´

17

 

int (octal)

021 (attention)

 

int (hexadecimal)´

0x11

 

long

17l

 

long long

17ll

 

float

-47.1f

-6.2e-2f

double

-47.1

-6.2e-2

long double

-47.1l

-6.2e-2l

complex

(C99) 3.5+I*2.115

2.3          Domaine et representation machine des entiers´

2.3.1           Domaine des entiers non-signes et sign´     es´

Attributs unsigned et signed des types entiers ou caracteres pour indiquer si` le bit de poids fort est un bit de signe (cas par defaut pour les entiers).´ taille et domaine des entiers (depend de la machine) en base 2´

type

taille

unsigned

signed

char

1 octet

0 ? 255 = 28 ? 1

?128 ? +127

short

2 octets

0 ? 216 ? 1

?215 ? 215 ? 1

int

4 octets

0 ? 232 ? 1

?231 ? 231 ? 1

long

(4 a ou) 8 b octets

0 ? 264 ? 1

?263 ? 263 ? 1

 

a.   Machine 32 bits

b.  Machine 64 bits


Pour les entiers signes :´          (en base 2 et base 10)

Rappel : log10 2 ? 0,30          ?         210 = 1024 = 1010log10(2) ? 103

 

C

 

sur 32 bits = 4 octets

INT_MAX

231 ? 2 × 109

sur 64 bits = 8 octets

LONG_MAX

263 ? 8 × 1018

? C99 : types entiers etendus´ a nb d’octets impos` e ou´ a minimum impos`       e´ par exemple : int32_t ou int_least64_t

Depassement de capacit´      e en entier´    ? passage en negatif´

2.3.2         Representation machine des entiers´

Stockage en memoire´      ? representation binaire´

(representation exacte pour les entiers mais pas pour les flottants, en g´                                                                                                                                        en´                                                                                                                                    eral)´

Representation binaire dans le cas des entiers :´

–    entier non signe :´       decomposition de l’entier en base 2´

–    entier signe :´

–    bit de poids fort : 0 (entier positif) ou 1 (entier negatif),´

–    bits restants : decomposition de l’entier en base 2´

                (complementaire bit´   a bit + 1 pour les entiers n` egatifs)´

Exemples :

Entier non-signe´ i=10 code sur 4 octets : 00000000´                                                                                     000000000000000000001010.

Entier signe court´      j=-1 code sur 2 octets :´       1111111111111111.

2.3.3         Structure simplifiee de la RAM : d´      eclaration de deux entiers´

Declaration de deux variables :´

short int j; unsigned int i;

Structure simplifiee de la RAM´   au cours de l’execution de ces instructions´                                                                                                            :

sizeof (short int)                           sizeof (unsigned  int)

 

short int j;

                   &j                  adresse                                  &i

De maniere g` en´ erale :´

–    la memoire est segment´      ee´   (segment el´ ementaire : 1 octet)´

–    chaque segment est associe´ a une adresse`         (nombre entier long non-signe)´

En particulier, lors de la compilation de l’instruction de declaration :´

–    reservation d’un nombre d’octets qui d´        epend du type de la variable´

(sizeof(type) donne la taille en octets du type type)

–    attribution d’une adresse : celle associee au premier segment occup´  e´

(&i et &j sont les adresses de i et j ou` & est l’operateur adresse)´

–    si la variable n’a pas et´ e initialis´ ee sa valeur est indetermin´      ee (´ ?)

2.3.4            Structure simplifiee de la RAM : affectation de deux entiers´

Affectation des deux variables (apres d`     eclaration) :´

j=-1; i=10;

Structure simplifiee de la RAM´   au cours de l’execution de ces instructions´                                                                                                            :

sizeof (short int)                             sizeof (unsigned  int)

   

                         2.4          Valeurs maximales des entiers en C

/* programme limites-int-machine.c */

#include <stdio.h> #include <stdlib.h>

#include <limits.h> /* valeurs limites definies ici */ int main(void)

{

/* impression des valeurs limites des entiers sur la machine */

                                                          /* non-signes´ puis signes´ en decimal,´                                                   hexadecimal´                           et octal */

/* entier long */

printf("%-18s %20lu %16lx %22lo\n",

"Unsigned-Long-max", ULONG_MAX, ULONG_MAX, ULONG_MAX); printf("%-18s %20ld %16lx % 22lo\n",

"Long-max", LONG_MAX, LONG_MAX, LONG_MAX);

/* entier */

printf("%-18s %20u %16x % 22o\n",

"Unsigned-Int-max", UINT_MAX, UINT_MAX, UINT_MAX); printf("%-18s %20d %16x % 22o\n", "Int-max", INT_MAX, INT_MAX, INT_MAX);

/* entier court */

printf("%-18s %20hu %16hx % 22ho\n",

"Unsigned-Short-max", USHRT_MAX, USHRT_MAX, USHRT_MAX); printf("%-18s %20hd %16hx % 22ho\n",

"Short-max",SHRT_MAX, SHRT_MAX, SHRT_MAX); exit(0) ;

}

                         2.4.1            Valeurs maximales des entiers en C : machine 32 bits

Unsig-Lng-Lng-max 18446744073709551615 ffffffffffffffff 1777777777777777777777

Long-Long-max                                           9223372036854775807 7fffffffffffffff 777777777777777777777

Unsigned-Long-max

4294967295

ffffffff

37777777777

Long-max

2147483647

7fffffff

17777777777

Unsigned-Int-max

4294967295

ffffffff

37777777777

Int-max

2147483647

7fffffff

17777777777

Unsigned-Short-max

65535

ffff

177777

Short-max

32767

7fff

77777

                         2.4.2            Valeurs maximales des entiers en C : machine 64 bits

Unsig-Lng-Lng-max 18446744073709551615 ffffffffffffffff 1777777777777777777777

Long-Long-max                                           9223372036854775807 7fffffffffffffff 777777777777777777777

Unsigned-Long-max 18446744073709551615 ffffffffffffffff 1777777777777777777777

Long-max                                                    9223372036854775807 7fffffffffffffff 777777777777777777777

Unsigned-Int-max

4294967295

ffffffff

37777777777

Int-max

2147483647

7fffffff

17777777777

Unsigned-Short-max

65535

ffff

177777

Short-max

32767

7fff

77777


2.5 Domaine et precision des flottants´

2.5         Domaine et precision des flottants´

2.5.1        Codage des flottants

Un flottant est un nombre reel qui est d´  ecrit par (en d´ ecimal ici, en machine le´ codage est en binaire) :

–    un signe

–    une mantisse dont le nombre maximal de digits indique la precision´ – un exposant dont le nombre maximal de digits indique le domaine.

Lorsque le nombre maximal de digits est fixe et que l’exposant peut varier on parle´ de representation en´       virgule flottante.

Exemples en base 10 : (la mantisse m est telle que : 1 ? m < 10)

–    100,5 correspond a` 1,005 × 102

? exposant de 2 (1 digit) et mantisse m = 1,005 (3 digits)

–    1000000020 correspond a` 1,000000020 × 109

? exposant de 9 (1 digit) et mantisse m = 1,000000020 (9 digits)

En machine, un flottant code sur 4 octets est d´      ecompos´ e en :´

1 bit de signe, 23 bits pour la mantisse et 8 bits pour l’exposant.

2.5 Domaine et precision des flottants´

2.5.2         Domaine et precision des flottants´

–    Domaine fini comme pour les entiers : valeur finie de l’exposant

–    Precision´     limitee contrairement aux entiers : nombre fini de digits de la mantisse´

              (defini comme la plus grande valeur telle que´               

? les flottants ne peuvent etre reprˆ  esent´ es exactement, en g´ en´ eral.´

 

Application :

Pas de difference entre´    1000000020 et 1000000000 pour des variables du type float puisque la mantisse est de 9 digits alors que le type float autorise une precision de 7 digits au maximum.´

2.6       Constantes

2.6.1      Syntaxe

Attribut const devant le type

const int j = -1;

mais la non-modification des constantes (via une fonction notamment) n’est pas toujours respectee´ ? on pourra egalement utiliser la directive´  #define du preprocesseur´

#define J -1                               /* attention: pas de ; */

Le preprocesseur substituera´   J par -1 partout dans le programme mais la constante n’est plus typee´


2.6.2           Exemples d’attribut const en C

/* programme const.c */

#include <stdio.h> #include <stdlib.h> int main(void) {

const int i = 2 ; /* non modifiable */

i++ ;   /* => erreur a` la compilation */ printf("i vaut %d\n", i);

exit(0);

}

Avec gcc par exemple

const.c:9: error: increment of read-only variable ’i’

2.6.3            Exemples d’utilisation de #define

/* programme const2.c */

#include <stdio.h>

#include <stdlib.h>

#define J 2 /* preproc.´                             remplace J par 2 */

int main(void) {

                J=1 ;                             /* => erreur a` la compilation */

exit(EXIT_SUCCESS);

}

Avec gcc par exemple

const2.c:5: invalid lvalue in assignment

3     Operateurs´

Operateurs unaires : agissent sur un seul argument.´

Operateurs binaires : agissent sur deux arguments.´

Operateurs ternaires : agissent sur trois arguments.´

3.1       Operateur d’affectation´

Operateur binaire´    evalu´        e de droite´ a gauche :` lvalue = expression ? conversions implicites eventuelles´

(le terme de droite est converti dans le type du terme de gauche) En cas de conversion il est fortement conseille de l’expliciter :´ lvalue = (type) expression ? conversion explicite (operateur´        cast) Attention aux problemes d’`   etendue/pr´      ecision´     lors de l’affectation.


3.1 Operateur d’affectation

/* programme precision.c */

#include <stdio.h> #include <stdlib.h> int main(void) {

int a=123456789; float b=0.123456789f; float c;

printf("float: %d\nint: %d\n",sizeof(float),sizeof(int)); c=(float)a;

printf("%d %.10g %.10g\n",a,c,b); /* passage en double

pour plus de precision - attention au f*/

exit(EXIT_SUCCESS);

}

Resultat´  a l’ex` ecution :´

float : 4 int : 4

123456789 123456792 0.123456791

3.2 Operateurs algebriques´

3.2      Operateurs alg´  ebriques´

   

langage C

addition

 

+

soustraction

 

-

multiplication

 

*

division

 

/

reste modulo

 

%

Remarques :

–    operateurs´  +, -, *, / et % : operateurs binaires agissant de gauche´         a droite.`

–    attention a la diff` erence entre les op´ erateurs alg´     ebriques binaires´     - et + d’une part, et les operateurs unaires de signe :´ - (oppose) et´ + d’autre part.

–    el´ evation´   a la puissance au moyen de la fonction`     pow(x,y) (inclure le fichier tgmath.h dans le code source et ajouter l’option -lm a la compilation).`

3.2 Operateurs algebriques´

3.3        Operateurs de comparaison´            3.4           Operateurs logiques´

 

resultat´

 

entier

               inferieur´ a`

 

inferieur ou´  egal´ a`

 

<=

                      egal´ a`

 

==

superieur ou´  egal´                             a`

 

>=

             superieur´ a`

 

different de´

 

!=

 

ET

 

&&

OU

 

||

NON

 

!

Attention : ne pas confondre l’operateur test´ d’egalit´ e´ == avec l’operateur d’affectation´ =.


                                                                                                                                                                                        3.5 Incrementation et d´                                                                                                                                                                           ecr´                                                                                                                                                     ementation en C´

3.5       Incrementation et d´ ecr´ ementation en C´

–    incrementation´



–    post-incrementation :´      i++ incremente´      i d’une unite,´ apres` evaluation de l’expression´

p=2; n=p++; donne n=2 et p=3

–    pre-incr´   ementation :´    ++i incremente´      i d’une unite,´ avant evaluation de l’expression´

p=2; n=++p; donne n=3 et p=3

–    decr´ ementation´

–    post-decr´ ementation :´    i-- decr´ emente´    i d’une unite,´ apres` evaluation de l’expression´

p=2; n=p--; donne n=2 et p=1

–    pre-d´        ecr´  ementation :´    --i decr´ emente´    i d’une unite,´ avant evaluation de l’expression´

p=2; n=--p; donne n=1 et p=1

                                                                                                                                                                                                                                      3.6 Operateurs d’affectation compos´                                                                                                             ee en C´

3.6         Operateurs d’affectation compos´    ee en C´

Operateurs binaires :´ lvalue operateur´   = expression ? lvalue = lvalue operateur´         expression Exemples :

j += i

?

j = j + i

b *= a + c

?

b = b * (a + c)

3.7        Operateur d’alternative en C´

Operateur ternaire :´

exp1 ? exp2 : exp3 ? si exp1 est vraie, exp2

sinon exp3

Exemple :

c = (a>b) ? a : b affecte le max de a et b a` c

3.8 Operateurs agissant sur les bits´

3.8         Operateurs agissant sur les bits´

Le langage C possede des op`    erateurs de bas niveau travaillant directement sur les´ champs de bits.

 

signification

˜expr

negation´

expr1 & expr2

et

expr1 | expr2

ou

expr1 ˆ expr2

ou exclusif

expr1 << expr2

decalage´   a gauche de`     expr2 bits

expr1 >> expr2

decalage´    a droite de`      expr2 bits

3.9 Operateur sizeof en C´

3.9        Operateur sizeof en C´

Taille en octets d’un objet ou d’un type (resultat de type´          size_t).

Cet operateur permet d’am´  eliorer la portabilit´ e des programmes.´

sizeof (identificateur)

size_t n1; double a; n1 = sizeof(a);

sizeof(type) size_t n2; n2 = sizeof(int);

3.10      Operateur s´ equentiel´  , en C

expr1 , expr2 permet d’evaluer successivement les expressions´        expr1 et expr2.

Utilise essentiellement dans les structures de contr´        ole (ˆ if, for, while).

3.11      Operateurs´   & et * en C

&objet ? adresse de l’objet

*pointeur ? valeur pointee (indirection)´

                                                                                                                                                                                                                                   3.12 Priorites des op´                                                                                                                                erateurs en C´

3.12       Priorites des op´   erateurs en C´

–    operateurs sur les tableaux, fonctions, structures :´       [], (), ->, .

–    operateurs unaires´       +, -, ++, --, !, ˜, *, &, sizeof, (cast)

–    operateurs alg´     ebriques´  *, /, %

–    operateurs alg´     ebriques´  +, – operateurs de d´         ecalage´    <<, >>

–    operateurs relationnels´        <, <=, >, >=

–    operateurs relationnels´        ==, !=

–    operateurs sur les bits´ &, puis ˆ, puis |

–    operateurs logiques´ &&, puis || – operateur conditionnel´         ?:

–    operateurs d’affectation´      = et les affectations composees´

–    operateur s´ equentiel´ ,

? indiquer les priorites avec des parenth´     eses !`


4        Entrees et sorties standard´    el´ ementaires´

4.1    Gen´ eralit´ es´

Ecriture sur stdout = ecran :´

printf("format", liste d’expressions)

? afficher a l’` ecran (´       stdout) des messages et les valeurs des variables

Lecture depuis stdin = clavier :

scanf("format", liste d’adresses)

? lire du clavier (stdin) les valeurs des variables

? stocker ces valeurs aux adresses specifi´          ees par les arguments´

Attention : operateur adresse´        & dans scanf (en gen´  eral).´

Format : specifier le type par´       % de chaque variable

(gabarit optionnel)

Specifier´     \n en sortie pour changer de ligne

                                                                                                                                                                                                                                     4.1 Gen´                                                                                                                                                                       eralit´                                                                                                                                                                            es´

/* programme printf_scanf.c */

#include <stdio.h> /* contient printf et scanf */

#include <stdlib.h>

int main(void) {

int i; float x; double y; printf("Entrer un entier\n");

/* Ne pas oublier l’operateur´ adresse dans scanf ! */ scanf("%d", &i); printf("La valeur de i est %d\n", i);

printf("Entrer deux réels: float, double\n"); scanf("%g %lg", &x, &y);

/* Difference´      de format scanf/printf pour les flottants !*/ printf("Les valeurs de x et y sont %g et %g\n", x, y);

exit(EXIT_SUCCESS);

}

4.2 Formats d’affichage en C

4.2         Formats d’affichage en C

Attention : quelques differences entre´ scanf (type exact) et printf (conversion de type possible)

En sortie printf

Type

Format

char

%c

chaˆ?ne

%s

int/short

%d

unsigned int/unsigned short

%ud (%o, %x)

long

%ld

unsigned long

%lu (%lo, %lx)

long long

%lld

unsigned long long

%llu

float/double

(%e, %f) %g

long double

(%le, %lf) %lg

4.2 Formats d’affichage en C

En entree´ scanf

Type

Format

char

%c

short

%hd

unsigned short

%hu (%ho, %hx)

int

%d

unsigned int

%u (%o, %x)

long

%ld

unsigned long

%lu (%lo, %lx)

long long

%lld

unsigned long long

%llu (%llo, %llx)

float

(%e, %f) %g

double

(%le, %lf) %lg

long double

(%Le, %Lf) %Lg

4.3 Gabarits d’affichage en C

4.3         Gabarits d’affichage en C

4.3.1        Cas des entiers

Structure gen´ erale :´  %wd ou` w est le gabarit d’affichage.

Le gabarit est un nombre qui indique la largeur minimale du champ d’affichage.

Exemple : %5d ? au moins 5 caracteres sont r`        eserv´ es´ a l’affichage de l’entier.`

4.3.2        Cas des flottants

Structure gen´ erale :´    %w.pf ou` w indique la largeur minimale du champ d’affichage

(incluant le point decimal) dont´        p caracteres sont r`  eserv´ es´    a la partie d`      ecimale´ (precision).´

Exemple : %5.3f ? 5 caracteres sont r`   eserv´        es´    a l’affichage du flottant dont 3` pour la partie decimale.´


5       Structures de controleˆ

Par defaut :´      execution des instructions une fois,´ dans l’ordre dans lequel elles apparaissent dans le code source ? trop restrictif.

Structures de controle (ˆ  flow control en anglais) : permettent de modifier le cheminement lors de l’execution des instructions.´

Differents types de structures de contr´   ole :ˆ

–    execution conditionnelle (´   if) ou aiguillage (switch) dans les instructions,

–    iteration de certains blocs (´ while, for)

–    branchements (break, continue)

Elles peuvent etreˆ    combinees´   au sein d’un meme programme.ˆ

5.1        Structure if

Permet de choisir quelles instructions vont etre exˆ      ecut´ ees :´

if (expression){

bloc d’instructions;

/* si l’expression est vraie */ }

(expression :

-    vraie si elle est !=0

-    fausse si elle est ==0)

if(expression){

bloc d’instructions 1;

/* si l’expression est vraie */

} else {

bloc d’instructions 2;

/* si l’expression est fausse */

}

Des if { } else { } peuvent etre imbriquˆ                                es :´

 

if (expression_1){

/* si expression_1 est vraie */

} else {

if (expression_2){

/* si expression_1 est fausse et expression_2 est vraie */

} else {

/* si expression_1 et expression_2 sont fausses */

} }

Attention dans ce cas a bien`     respecter l’indentation pour montrer la structuration du programme.

5.1.1         Exemple de if

/* programme if.c */

#include <stdio.h>

#include <stdlib.h>

/* structure  if else affichage du max de deux nombres */

int main(void){

int i, j, max;

printf("entrer i et j (entiers)\n"); scanf("%d %d", &i, &j);

if (i >= j) { /* bloc d’instructions */

printf(" i >= j \n"); max = i;

}

                 else {                               /* bloc d’instructions */

max = j;

}

printf(" i= %d, j= %d, max = %d\n", i, j, max);

exit(0);

}


                           5.2            Structure switch (pas avec des flottants)

Aiguillages multiples :

switch (expression_entière) {

case sélecteur1 :

instructions; /* si expression == selecteur1 */ break; /* optionnel */ case sélecteur2 :

instructions; /* si expression == selecteur2 */ break;    /* optionnel */

                                              default :                              /* optionnel */

instructions; /* dans tous les autres cas */

}

selecteur´    : une expression constante entiere ou caract`    ere (ex :`   3 ou ’z’) Si on ne precise pas´ break, on passe par toutes les instructions suivant le cas selectionn´ e.´

                           5.2.1           Exemples de switch-case

/* programme case.c */

#include <stdio.h>

#include <stdlib.h>

/* structure switch case */ int main(void)

{

int i ; printf(" entrer un entier : "); scanf("%d", &i); printf("\n i = %d \n", i); switch (i) {       /* debut´     de bloc */

case 0 :

printf(" i vaut 0 \n");

                                                               break;                           /* necessaire ici ! */

case 1 :

printf(" i vaut 1 \n"); break;  /* necessaire ici ! */ default :

printf(" i différent de 0 et de 1 \n");

                                      }                                               /* fin de bloc */

exit(0);

}

/* programme case1.c */

#include <stdio.h>

#include <stdlib.h>

/* exemple d’utilisation de la structure case sans break

* pour "factoriser des cas" et les traiter en commun

*/ int main(void)

{

char c ;

printf("entrer un caractère: est-ce une ponctuation double ?"); scanf("%c", &c);

printf("\n caractère = %c \n", c); switch (c) { case ’?’ : case ’!’ : case ;:

case ’:’ :

printf("ponctuation double \n"); break ; default :

printf("autre caractère \n");

} exit(0) ;

}


                           5.3        Structures iteratives ou boucles´

                            Elles permettent de rep´   eter plusieurs fois un bloc d’instructions.´

                           5.3.1      Boucle definie (´   for)

Quand le nombre de rep´ etitions est´ connu, on utilise for :

for (expr-1; expr-2; expr-3) {

instructions ;

}

boucle

for (expr-1; expr-2; expr-3) { instructions ;

}

–    expr-1 est effectue´ une fois avant l’entree dans la boucle (g´         en´    eralement´ initialisation d’un compteur de tours)

–    expr-2 est une condition ”tant que”, evalu´    ee´    a chaque d`         ebut de r´ ep´    etition´

                                  (gen´ eralement test sur le compteur de tour)´

–    expr-3 est effectue´ a la fin de chaque r`         ep´        etition (g´ en´    eralement incr´ ementation´ du compteur de tours)

                           5.3.2         Exemple de boucle for

/* programme for.c */

#include <stdio.h>

#include <stdlib.h>

/* affichage des entiers impairs inferieurs´ a` un entier donne´ mise en oeuvre de la structure "for" */

int main(void){

int i, m = 11;

printf("affichage entiers impairs <= %d \n", m);

for (i = 1; i <= m; i = i + 2){ /*bloc d’instructions repetees*

printf("%d \n", i);

} exit(0);

}

                           5.3.3      Boucle indefinie (´         while et do while)

Quand le nombre de rep´     etitions (it´        erations) est a priori inconnu, on utilise´       while (ou do while) :

while (expr) {

instructions ;

}

tant que

while (expr) {

instructions ;

}

faire

do {

instructions ;

tant que

} while (expr);

–    Dans le cas de la boucle while : les instructions sont rep´       et´ ees ”´  tant que” expr est vraie.

–    Dans le cas de la boucle do while :

les instructions sont execut´    ees´  au moins une fois, puis rep´ et´ ees ”tant que”´       expr est vraie.

Attention aux boucles infinies si expr est toujours vraie.

                           5.3.4         Exemple de boucle while

/* programme while.c */

#include <stdio.h>

#include <stdlib.h>

 

/* mise en oeuvre de la structure "while"

*/

int main(void){

int i, m = 11 ;

printf("affichage entiers impairs <= %d \n", m);

i = 1 ;

                                                       while ( i <= m ) {                     /* bloc d’instructions repetees*/

printf(" %d \n", i); i += 2;

} exit(0) ;

}


5.4       Branchements

Les branchements permettent de modifier le comportement des boucles.

bouclage anticipe´

continue;

sortie anticipee´

break;

branchement

goto étiquette;

 

                L’etiquette est un identificateur suivi de´    : en tete d’instruction.ˆ

5.4.1          Exemple de continue

/* programme recycle.c */

#include <stdio.h>

#include <stdlib.h>

 

/* recyclage anticipe´ int main(void)

{

int i = 0 , m = 11; while ( i < m ) { i++;

via "continue" */

if ((i % 2) == 0 ) continue ; /* rebouclage si i pair */ printf("%d \n", i) ;

} exit(0) ;

}

5.4.2         Exemple de break

/* programme break.c */

#include <stdio.h> #include <stdlib.h>

int main(void){ // utilisation de break int i = 1, m = 11 ;

while (1) {       /* a priori boucle infinie */ printf(" %d \n", i); i += 2; if (i > m) {

break; // sortie de la boucle

} } exit(0) ;

}


6        Introduction aux pointeurs

6.1     Inter´ et des pointeursˆ

Motivation :

En C, les pointeurs sont indispensables, notamment pour leur utilisation en lien avec les fonctions (mais aussi tableaux, allocation dynamique, etc.)

Definition :´

Les pointeurs sont des variables contenant l’adresse d’autres variables d’un type donne.´

Finalite :´

Un pointeur permet d’agir indirectement sur une variable : via son adresse, au lieu d’agir directement sur la variable : via son identifiant.

6.2       Declaration et affectation´

6.2.1          Declaration d’une variable ordinaire (rappel)´

short int i; float x; ? reservation d’une´                               zone memoire´                                                                                                                               :

–    sa taille : depend du type de la variable d´    eclar´         ee´

(sizeof(type) : taille en octets du type type)

–    son emplacement : adresse du premier octet sur lequel la variable est stockee´

(&var : adresse de la variable var)

sizeof (short int)                                         sizeof (float)

   

6.2.2          Affectation d’une variable ordinaire (rappel)

i=10; x=0.2f;

= stocker la valeur dans la zone memoire r´      eserv´ ee.´

Attention : avant initialisation (= premiere affectation) la valeur d’une variable est` indetermin´   ee´

sizeof (short int)                                         sizeof (float)

 

i=10;

x=0.2f;

 

Pour simplifier : representation binaire des variables stock´         ees n’est pas montr´                                                                                                                       ee.´

6.2.3        Declaration d’un pointeur´

short int *pti; float *ptx;

= reservation d’une zone m´    emoire pour stocker´    l’adresse de la variable pointee´

Remarques :

–    comme toute autre variable un pointeur possede lui-m`        eme une adresseˆ

–    contrairement aux variables ordinaires la taille d’un pointeur est fixe

             (elle ne depend pas du type de la variable point´   ee)´

                               short int *pti;                           float *ptx;

 

&pti

 &ptx

sizeof (short int*)

sizeof (float*)

 

Visualisation des pointeurs et des variables ordinaires :

sizeof (short int)                                         sizeof (float)

 

                                     i                                                                    x

 

                 &i                  adresse                                 &x

                                                                                  pti                                            ptx

 

                                &pti                                 &ptx

                                                  sizeof (short int*)                    sizeof (float*)

 

6.2.4        Affectation d’un pointeur

Affecter une adresse a un pointeur :`

pti=&i; ptx=&x;

= stocker l’adresse memoire d’une variable dans la zone m´      emoire r´ eserv´        ee lors de´ la declaration du pointeur.´

On dit que :

(le pointeur) pti pointe sur (la variable) i, (le pointeur) ptx pointe sur (la variable) x.

Attention :

–    il faut que les variables i et x aient et´ e d´    eclar´         ees au pr´ ealable.´

–    comme pour une variable ordinaire l’adresse contenue dans le pointeur est indetermin´     ee avant l’initialisation du pointeur´

? initialiser un pointeur avant de le manipuler (cf. dissociation)

Visualisation de l’affectation des pointeurs : pti=&i; ptx=&x;

sizeof (short int)                                         sizeof (float)

 

                                     i                                                                    x

   

Visualisation apres affectation (`           pti pointe sur i et ptx pointe sur x) :

sizeof (short int)                                             sizeof (float)

 

&pti

 &ptx

sizeof (short int*)

sizeof (float*)

                                   i                                                                x

   

Affectation d’un pointeur de float : pt=&x; (pt pointe sur x)

                         sizeof (float)                                 sizeof (float)

 

                                                     x                                                       y

     

Re-affectation d’un pointeur de float : pt=&y; (pt pointe sur y)

                         sizeof (float)                                 sizeof (float)

 

                                                     x                                                       y

     

                           6.3      Indirection (operateur´   ?)

L’indirection est l’operation permettant d’´ acceder´   a la variable point`     ee´   via le pointeur (donc indirectement).

*pti=4;

                                  La variable sur laquelle pointe pti vaut desormais´       4.

                              Remarque : cette operation est d´ esastreuse si le pointeur n’a pas´                                     et´ e initialis´                                                    e´

                               ? modification non-volontaire d’une variable, acces`     a une zone m`                                               emoire interdite´

(segmentation fault)

La declaration d’un pointeur :´

double *ptd;

peut se lire *ptd est un double

/* programme indirection.c */

#include <stdio.h>

#include <stdlib.h>

/* modification de la valeur d’une variable */ int main(void) {

                        short int i;                                    /* declaration de i */

short int *pti=&i; /* declaration + affectation de pti */

i=10; /* initialisation de i par acces direct */ printf("La valeur initiale de i est %d\n", i);

*pti=4; /* modification indirecte de i */ printf("La valeur finale de i est %d\n", i);

exit(EXIT_SUCCESS);

}

Visualisation de la modification indirecte d’une variable (*pti=4;) :

   

&pti

sizeof (short int*)

 

6.4 Initialisation des pointeurs et dissociation

                           6.4          Initialisation des pointeurs et dissociation

Declarer un pointeur sans l’initialiser peut entrainer des erreurs.´

La dissociation permet de dissocier un pointeur de la variable vers laquelle il pointe :

*pt=NULL;

                             ? a utiliser lors de l’initialisation par d`  efaut des pointeurs.´

6.4 Initialisation des pointeurs et dissociation

/* programme erreur-pointeur.c */

           /* exemple de pointeur non initialise´ => erreur d’acces` memoire´                                                                                        */

#include <stdio.h> #include <stdlib.h> int main(void){ int i;

/* suivant l’ordre *pi, *pj => ? mais *pj, *pi => pj=0 erreur */ int *pj, *pi;

pi = &i; /* pi devient l’adresse de l’entier i */ /* affichage des valeurs des pointeurs pi et pj */ printf("pi=%lu, pj=%lu\n", (unsigned long int) pi,

(unsigned long int) pj);

/* acces` a` une zone interdite quand on affecte 2 a` l’adresse pj */

*pj = 2;       /* segmentation fault */ i = 1;

printf("i= %d, *pi= %d , *pj= %d\n", i, *pi, *pj); exit(EXIT_SUCCESS);

}

6.5 Bilan concernant la syntaxe des pointeurs

                           6.5           Bilan concernant la syntaxe des pointeurs

type

*ptr;

declaration sans initialisation (´       deconseill´ e´)

type

*ptr=NULL;

declaration avec initialisation (´      conseille´)

type

var;

cible

type

*ptr=&var;

declaration´ avec initialisation (conseille´ a condition que la cible ait d`   ej´ a` et´ e´ declar´ ee´ )

ptr = &var ;

ptr pointe sur var

*ptr

variable pointee par´        ptr

ptr = NULL ;

dissocier

6.6 Tailles des types de base et adresses en C

                           6.6           Tailles des types de base et adresses en C

--------------------------------------------------------------------

Processeur 32 bits AMD

-------------------------------------------------------------------Tailles en octets via sizeof :

char =                                        1 short int =                      2 int =                                       4 long int =      4

Adresses converties en unsigned long int :

                 c =3219368569                    s =3219368562                    i =3219368548          l =3219368536

&c[0]=3219368569 &s[0]=3219368562 &i[0]=3219368548 &l[0]=3219368536

&c[1]=3219368570 &s[1]=3219368564 &i[1]=3219368552 &l[1]=3219368540 &c[2]=3219368571 &s[2]=3219368566 &i[2]=3219368556 &l[2]=3219368544

Tailles en octets via sizeof :

float =                                    4 double =                                      8 long double = 12 int * =                   4

Adresses converties en unsigned long int :

                 f =3219368524                    d =3219368496                   L =3219368448       pi =3219368436

&f[0]=3219368524 &d[0]=3219368496 &L[0]=3219368448 &pi[0]=3219368436 &f[1]=3219368528 &d[1]=3219368504 &L[1]=3219368460 &pi[1]=3219368440 &f[2]=3219368532 &d[2]=3219368512 &L[2]=3219368472 &pi[2]=3219368444

6.6 Tailles des types de base et adresses en C

--------------------------------------------------------------------

Processeur 64 bits AMD

-------------------------------------------------------------------Tailles en octets via sizeof :

char =                                        1 short int =                      2 int =                                       4 long int =      8

Adresses converties en unsigned long int :

c =140735477551008          s =140735477550992       i =140735477550976       l =140735477550944 &c[0]=140735477551008 &s[0]=140735477550992 &i[0]=140735477550976 &l[0]=140735477550944

&c[1]=140735477551009 &s[1]=140735477550994 &i[1]=140735477550980 &l[1]=140735477550952 &c[2]=140735477551010 &s[2]=140735477550996 &i[2]=140735477550984 &l[2]=140735477550960

Tailles en octets via sizeof :

float =                                    4 double =                                      8 long double = 16 int * =                   8

Adresses converties en unsigned long int :

f =140735477550928           d =140735477550896      L =140735477550848       pi =140735477550816 &f[0]=140735477550928 &d[0]=140735477550896 &L[0]=140735477550848 &pi[0]=140735477550816 &f[1]=140735477550932 &d[1]=140735477550904 &L[1]=140735477550864 &pi[1]=140735477550824 &f[2]=140735477550936 &d[2]=140735477550912 &L[2]=140735477550880 &pi[2]=140735477550832


             7       Fonctions en C

              7.1    Gen´ eralit´ es´

En C, les fonctions sont indispensables :

? la fonction principale main existe obligatoirement.

Une fonction sert a :`

–    rendre un programme plus lisible,

–    factoriser des operations r´     ep´    etitives.´

Une fonction en C est proche d’une fonction mathematique :´

–    elle admet une liste d’arguments formels,

–    elle renvoie une valeur, le type de la fonction correspond au type de la valeur de retour.

Distinguer :

–    arguments formels ou muets (dummy) dans la definition de la fonction,´ – arguments effectifs dans l’appelant.

              7.2       Definition d’une fonction´

               Toute fonction possede un ent`   ete et un corps et est de la forme :ˆ

type_retour nom_fonction(type1 arg1, type2 arg2, )

{

/* declaration de variables locales */

/* liste d’instructions */

/* instruction de retour avant de quitter la fonction */

}

Remarque importante : le C n’autorise pas l’imbrication des fonctions !

? la definition d’une fonction doit´   etre placˆ           ee en dehors de celle de toute autre fonction.´

            L’enteteˆ   d’une fonction est de la forme :

type_retour nom_fonction(type1 arg1, , typen argn)

ou :`

– type_retour est le type de la valeur de retour de la fonction, – nom_fonction est l’identifiant de la fonction,

–    arg1, , argn sont les arguments formels (ou muets) de la fonction de types respectifs type1, , typen.

                  Le corps d’une fonction est place entre´      { et } :

–    il commence par la declaration de´ variables locales a la fonction`         ,

–    il se poursuit par une liste d’instructions propres a la fonction,`

–    il se termine une instruction de retour a la fonction appelante`     : return expression_de_retour ;

                      ou le type de` expression_de_retour definit celui de la fonction.´

              7.2.1           Exemples de fonctions renvoyant une valeur

Exemple d’une fonction de nom fonc, de type float, a un argument de type` int et ne possedant pas de variable locale :´

float fonc(int x)

/* entete de la fonction */

{

/* corps de la fonction */

return x/2.f ;

/* instruction de retour */

}

Remarque importante : le corps d’une fonction doit au moins contenir une instruction de retour

Exemple d’une fonction de nom som, de type int, a un argument de type` const int et possedant deux variables locales´       i et s de type int :

int som(const int p) /* entete de la fonction */

              {                                                               /* corps de la fonction */

/* somme des p premiers entiers */ int i , s; /* declaration des variables locales */

                             s = 0;                           /* liste d’instructions */

for (i = 0; i <= p; i++){ s += i;

}

return s ; /* instruction de retour */

}

              7.2.2          Exemple d’une fonction sans retour

Une fonction sans retour est une fonction a` effet de bord (side effect)

? de type void

void som(const int p) /* entete de la fonction */

{                                                                /* corps de la fonction

/* somme des p premiers entiers */ int i , s; /* variables locales */

s = 0;

for (i = 0; i <= p; i++){

*/

s += i;

}

/* l’affichage est un effet de bord: */ printf("Somme des %d premiers entiers: %d\n", p, s) ;

return ; /* aucune valeur rendue */

}

              7.2.3          Exemple d’une fonction sans argument

Une fonction sans argument est une fonction dont l’argument est de type void :

int main(void) /* entete de la fonction */

              {                                                 /* corps de la fonction */

return 0 ; /* valeur de retour */

}


7.3 Appel d’une fonction

7.3        Appel d’une fonction

L’appel d’une fonction se fait au moyen de l’expression :

nom_fonction(para1, , paran)

ou le nombre, l’ordre (et le type, cf. conversions implicites) des param`      etres effectifs :` para1, , doivent correspondre a ceux des arguments formels :`  arg1, , donnes dans l’ent´      ete de la fonction.ˆ

7.3.1          Appel d’une fonction avec retour

Si on exploite la valeur de retour, l’appel se fait au moyen de l’instruction :

var = nom_fonction_avec_retour(para1, , paran) ;

ou` var est une variable du meme type que la valeur de retour de la fonction.ˆ

7.3.2          Appel d’une fonction sans retour

L’appel se fait au moyen de l’instruction :

nom_fonction_sans_retour(para1, , paran) ;

7.3 Appel d’une fonction

7.3.3          Appel d’une fonction sans argument

Il faut garder les parentheses suivant le nom de la fonction.`

Fonction avec retour : l’appel se fait, en gen´  eral, au moyen de l’instruction :´

var = nom_fonction_sans_argument() ;

ou` var est une variable du meme type que la valeur de retour de la fonction.ˆ

Fonction sans retour : l’appel se fait au moyen de l’instruction :

nom_fonction_sans_argument() ;

7.4        Declaration d’une fonction´

Une fonction doit etre dˆ    eclar´ ee (et/ou d´ efinie) avant d’´ etre appelˆ                                                    ee.´

La declaration permet au compilateur de :´

–    verifier la concordance entre le nombre d’arguments dans l’appel et dans la´ definition,´

–    de mettre en place des conversions implicites (pour les arguments et la valeur de retour) entre l’appel et la definition.´


7.4.1         Declaration au moyen d’un prototype (m´     ethode conseill´ ee)´

Le prototype d’une fonction definit son type, son nom, ainsi que le nombre et le type´ de ses arguments :

type_retour nom_fonction(type1 arg1, , typen argn) ;

? prototype = entete +ˆ ;

Le prototype doit etre placˆ       e´ avant la definition de la fonction et´ (par ordre croissant de pref´ erence´ ) :

–    au sein d’une fonction ? visible uniquement dans cette fonction

–    au debut du fichier (en dehors de toute fonction) (´       conseille pour les d´ ebutants´ )

? visible dans tout le fichier (declaration globale)´

–    dans un fichier d’entete (ouˆ         include ), d’extension .h, inclus par

#include "fichier.h"

? visible dans tous les fichiers qui incluent ce fichier .h

La definition peut´     etre placˆ ee n’importe o´ u` apres`   le prototype et en dehors de toute autre fonction.

7.4.2 Exemple de declaration et d’appel d’une fonction´ La fonction principale est la fonction appelante :

#include <stdio.h> /* pour les entrees/sorties´ */ #include <stdlib.h>       /* par exemple pour exit      */

int som(const int p) ;                              /* declaration´    de la fonction somme */

int main(void){                           /* fonction principale (sans param.) */

int s ;

                    s = som(5) ;                                /* appel de la fonction somme */

printf("somme entiers de 1 à 5 = %d\n", s);

printf("somme entiers de 1 à 9 = %d\n", som(9)); /*autre appel*/

                   exit(0) ;                                 /* renvoie a` unix un status 0 (OK) */

}

7.4.3        Declaration au moyen de la d´   efinition (m´ ethode d´ econseill´                                          ee)´

La definition tient lieu de d´  eclaration´             ? definir la fonction avant de l’appeler.´

Probleme`  :

cette methode est d’autant plus limit´       ee que le nombre de fonctions augmente ; une´ fonction peut en appeler d’autres qui en appellent d’autres, etc.


7.5             Quelques fonctions (standards) du C

7.5.1             La fonction principale : main

La fonction main est la fonction principale d’un programme C :

celle par ou le programme d`      ebute´ ? elle existe obligatoirement

L’entete de la fonction principale est gˆ     en´ eralement de la forme :´

int main(int argc, char *argv[])

– valeur de retour du type int

? le corps de la fonction doit se terminer par une instruction de retour :

return 0;        qui renvoie au shell la valeur 0 ou bien : exit(EXIT_SUCCESS); qui renvoie au shell la valeur EXIT_SUCCESS (=0)

arguments : rec¸us de l’interpreteur de commande au lancement de l’ex´                                                                                                                             ecution.´

Utilisation sans argument ? int main(void)

7.5.2            La fonction exit

Fonction de la bibliotheque standard.`

La fonction exit met fin a un processus (l’ex`            ecution du programme).´

Son prototype est dans le fichier : stdlib.h

? directive preprocesseur (compilation) :´                   #include <stdlib.h>

Prototype : void exit(int status) ;

–    valeur de retour du type void

–    argument status de type int et dont les valeurs sont gen´         eralement :´

–    0 ou EXIT_SUCCESS pour signaler que le programme se termine normalement

–    1 ou EXIT_FAILURE pour signaler un probleme`

Remarque : Le fichier stdlib.h contient aussi les constantes pred´    efinies :´

EXIT_SUCCESS, EXIT_FAILURE, NULL, etc

7.5.3                Les fonctions printf et scanf

Les fonctions printf et scanf sont des fonctions d’entree-sortie standard.´

Leur prototype est dans le fichier : stdio.h

? directive preprocesseur (compilation) :´                  #include <stdio.h>

Prototypes : int printf(const char* format, ) ; int scanf(const char* format, ) ;

–    valeur de retour du type int

–    printf : le nombre de caracteres`    ecrits si tout se passe bien´ ou un nombre negatif en cas d’erreur.´

–    scanf : le nombre de variables effectivement lues si tout se passe bien ou un nombre negatif en cas d’erreur.´

–    nombre variable d’arguments : , dont au moins une chaˆ?ne de caracteres`

Remarque : si on ne s’interesse pas aux valeurs de retour de ces fonctions, elles´ peuvent etre appelˆ       ees comme des fonctions sans retour.´

7.5.4            Les fonctions mathematiques (´      pow, fabs, )

Ce sont les fonctions trigonometriques, exponentielles, logarithmiques, etc ´ ? importance capitale pour les applications numeriques´ .

Les prototypes sont contenus :

–    en C89, dans le fichier de librairie standard math.h

                 ? directive preprocesseur (compilation) :´           #include <math.h>

–    en C99, dans le fichier de librairie standard tgmath.h

                 ? directive preprocesseur (compilation) :´       #include <tgmath.h>

(tgmath.h permet d’utiliser le type complex absent de math.h)

Remarque importante : pour pouvoir utiliser les fonctions de la bibliotheque` mathematique il faut utiliser l’option´       -lm de gcc (edition des liens).´ Exemple de la fonction puissance : pow

En C, il n’y a pas d’operateur puissance mais une fonction puissance :´

xy ? pow(x,y)

Prototype : double pow(double x, double y) ;

Cette fonction peut etre utilisˆ     ee avec des param´  etres de type entier ou float`

? le processeur se charge de convertir les parametres en double`

? le resultat est aussi un double´

Exemple de la fonction valeur absolue : fabs

Fonction renvoyant la valeur absolue d’un nombre reel :´

|x| ? fabs(x)

Prototype : double fabs(double x) ;

Remarque importante : ne pas confondre fabs et abs

Prototype de abs : int abs(int i) ;

7.5.5 Exemple d’un programme utilisant math.h

/* programme math_exemples.c */

#include <stdio.h>

/* entrees-sorties */

#include <stdlib.h>

/* exit et EXIT_SUCCESS */

#include <math.h>

/* fonctions mathematiques */

int main(void){ int i=2; double x=-0.5, y;

y = abs(x);       /* attention: conversion en entier */ printf("Valeur absolue entiere de %g: %g\n", x, y);

y = fabs(x); printf("Valeur absolue reelle de %g: %g\n", x, y); y = pow(x,(double) i); printf(" %g puissance %d = %g\n", x, i, y);

exit(EXIT_SUCCESS);

}

Compilation avec :

gcc-mni math_exemples.c -lm -o math_exemples.x

Resultat de l’ex´    ecution :´

Valeur absolue de -0.5: 0

Valeur absolue de -0.5: 0.5

-0.5 puissance 2 = 0.25

7.5.6              Liste des fonctions mathematiques standards´

C89

C99 avec tgmath.h

remarques

l/ll/abs(n) (i)

 

abs pour les entiers

fabs/f/l(a) (r)

fabs(a) (a)

C99 + tgmath.h

 

cabs(c) (z) C99 + fabs

complex.h

sqrt/f/l(a) (r)

csqrt(a) (z) sqrt

 

pow/f/l(a,b) (r)

cpow/f/l(a,b) (z)pow

 

exp/f/l(a) (r)

cexp/f/l(a) (z)exp

 

log/f/l(a) (r)

clog/f/l(a) (z)log

 

log10/f/l(a)

cimag/f/l(a) (z)

C99 +complex.h

 

conj/f/l(a) (z) conj

C99 +complex.h

ceil/f/l(a)

ceil

resultat flottant´

floor/f/l(a)

floor

resultat flottant´

n%p

copysign/f/l(a,b)

operateur´

C89

C99 avec tgmath.h

remarques

 

cos/f/l(a) (r)

ccos/f/l(a) (z) cos

 

 

cosh/f/l(a) (r)

ccosh(a) (z) cosh

 

 

acos/f/l(a) (r)

cacos/f/l(a) (z) acos

C99 + tgmath.h

 

sin/f/l(a) (r)

csin/f/l(a) (z) sin

 

 

sinh/f/l(a) (r)

csinh/f/l(a) (z) sin

C99 + tgmath.h

 

asin/f/l(a) (r)

casin/f/l(a) (z) asin

 

 

tan/f/l(a) (r)

ctan/f/l(a) (z)tan

 

 

tanh/f/l(a) (r)

ctanh/f/l(a) (z) tanh

C99 + tgmath.h

 

atan/f/l(a) (r)

catan/f/l(a) (z) atan

 

 

atan2/f/l(a,b)

atan2

 

 

           

Remarque : acces via le shell` a la documentation en ligne au moyen de la` commande :

man 3 nom_fonction


                           7.6       La portee des variables´

La portee (´ scope en anglais) d’une variable correspond a la portion de programme` sur laquelle la variable est definie.´

Pour un programme donne, on distingue deux cas :´

–    variable globale : variable dont la portee s’´        etend´       a l’ensemble du programme,` – variable locale : variable dont la portee est limit´        ee´    a une portion du programme` (une fonction par exemple, voire un sous-bloc d’une fonctions).

Conseils pratiques :

–    les variables globales doivent rester des exceptions ? privilegier l’emploi de variables locales´    .

–    garder les memes notations pour les variables globales et localesˆ

(lettres minuscules)

                                        ? reserver les majuscules aux constantes pr´    eprocesseur´

                           7.6.1       Variables globales

                                Une variable globale est declar´     ee avant toute fonction´                                      (conseille´)

? portee : ensemble du programme´

? variable dite permanente ou statique : occupe la meme zone mˆ    emoire´ pendant toute l’execution du programme.´

                Remarque : une variable globale peut etre dˆ   eclar´ ee´          entre deux fonctions (deconseill´         e´)

                            ? portee : tout le programme´ suivant la declaration de la variable´

                           7.6.2       Variables locales

                                 Une variable locale a un bloc est d`    eclar´  ee dans ce bloc´

                           ? portee : r´   eduite au bloc dans laquelle elle est d´  eclar´ ee´

? variable dite temporaire ou automatique : la zone memoire occup´  ee par la´ variable est liber´ ee´ a la sortie du bloc.`

Remarque : le C99 autorise les declarations tardives´

                           ? la portee peut´ etre restreinteˆ  a un sous-bloc d’une fonction`

                           7.6.3           Exemple d’un programme utilisant une variable globale

/* programme global_exemple.c */

#include <stdio.h> /* pour les entrees/sorties´ */ #include <stdlib.h>       /* par exemple pour exit */

int n=5;   /* declaration variable globale n=5 */ int somme(void) ; /* declaration´   de la fonction somme */

int main(void){                                                   /* fonction principale */

                  int s ;                                  /* variable locale a main */

s = somme() ;    /* appel fonction somme sans argument */ printf("somme de 1 a %d = %d\n", n, s) ;

n++;    /* incrementation de la variable n */ s = somme() ; /* appel de la fonction somme */

printf("somme de 1 a %d = %d\n", n, s) ;

                   exit(0) ;                                 /* renvoie a` unix un status 0 (OK) */

}

int somme(void)                                                  /* definition´         de la fonction somme */

{

int i , sum ; /* variables locales a somme */ for (i = 0, sum = 0 ; i <= n ; i++) /* n: variable globale ! */

{

sum += i ;

}

                  return sum ;                             /* valeur rendue par la fonction */

}

                           Resultats´  :

somme de 1 a 5 = 15 somme de 1 a 6 = 21


                    7.7          Passage de parametres dans une fonction`

Par passage de parametre on entend la transmission de la valeur d’une variable` de la fonction appelante vers la fonction appelee et vice-versa.´

Rappel :

L’appel d’une fonction se fait au moyen de l’expression :

nom_fonction(para1, , paran)

ou le nombre, l’ordre (et le type) des param`  etres :`       para1, , doivent correspondre a ceux des arguments :`   arg1, , donnes dans l’ent´      ete de la fonction.ˆ

Important : on utilise la terminologie suivante

–    Les parametres effectifs`    sont des variables de la fonction appelante.

–    Les parametres formels (ou muets)`   d’une fonction sont ceux qui figurent dans l’entete de la fonctionˆ    .

                    7.7.1           Exemple de passage par valeur : le faux echange´

/* programme faux-echange.c */

/* Fonctions : passage des arguments par valeur */

/* => pas de modification en retour */

#include<stdio.h>

#include<stdlib.h>

void echange(int a, int b) ; /* prototype */ void echange(int a, int b)    /* definition */

{ /* a et b:                           ˆ         ˆ parametres formels (muets) */

int c; /* variable locale a` la fonction */ printf("\tdebut echange : %d %d \n", a, b); printf("\tadresses debut echange : %p %p \n", &a, &b);

c = a; a = b; b = c; printf("\tfin echange : %d %d \n", a, b); printf("\tadresses fin echange : %p %p \n", &a, &b); return;

}

int main(void)

{

int n = 1, p = 5;

printf("avant appel : n=%d p=%d \n", n, p); printf("adresses avant appel : %p %p \n", &n, &p); echange(n,p); /* appel avec les parametres effectifs */ printf("apres appel : n=%d p=%d \n", n, p); printf("adresses après appel : %p %p \n", &n, &p); exit(0) ;

}

Les parametres muets,`  a et b, de la fonction echange stockent la valeur des parametres effectifs,`   n et p, de la fonction appelante :

? a et b sont une copie locale de la valeur de n et p, respectivement

? la copie disparaˆ?t au retour dans la fonction appelante

? les variables de la fonction appelante ne sont pas modifiees´      par la fonction appelee´

? on parle de passage par copie de valeur

avant appel : n=1 p=5

adresses avant appel : 0xbfd04174 0xbfd04170 debut echange : 1 5

adresses debut echange : 0xbfd04140 0xbfd04144 fin echange : 5 1

adresses fin echange : 0xbfd04140 0xbfd04144

apres appel : n=1 p=5

adresses après appel : 0xbfd04174 0xbfd04170

                    7.7.2          Le faux echange : visualisation de la RAM´       a l’ex`         ecution´

Dans la fonction main avant l’appel a la fonction`  echange : int n=1; int p=5;

 

Attention : pour simplifier, chaque segment represent´  e a la taille d’un entier.´ A l’appel de la fonction echange : passage par copie de valeur

                     ? echange de´   a et b

 

                     Au retour a la fonction`                  main les valeurs de n et p restent inchangees :´

 
                    7.7.3            Exemple de passage par adresse : le vrai echange´

/* programme echange.c */

/* Fonctions : passage des adresses en arguments (par valeur)

/* => modification en retour */

#include<stdio.h> #include<stdlib.h>

void echange(int *ptra, int *ptrb); /* prototype */ void echange(int *ptra, int *ptrb){ /* definition */

/* ptra et ptrb ˆ ˆ pointeurs sur des entiers */ int c; /* variable locale a` la fonction (pas pointeur) */ printf("\tdebut echange : %d %d \n", *ptra, *ptrb); printf("\tadresses debut echange : %p %p \n", ptra, ptrb); c = *ptra;

*ptra = *ptrb;

*ptrb = c;

printf("\tfin echange : %d %d \n", *ptra, *ptrb); printf("\tadresses fin echange : %p %p \n", ptra, ptrb); return;

} int main(void)

{

int n = 1, p = 5;

printf("avant appel : n=%d p=%d \n", n, p); printf("adresses avant appel : %p %p \n", &n, &p); echange(&n, &p); /* appel avec les adresses */ printf("apres appel : n=%d p=%d \n", n, p); printf("adresses après appel : %p %p \n", &n, &p); exit(0) ;

}

Les parametres muets,`  ptra et ptrb, de la fonction echange stockent l’adresse des parametres effectifs,`       n et p, de la fonction appelante :

? ptra et ptrb sont des pointeurs sur n et p, respectivement

                       ? la fonction appelee travaille sur la m´  eme zone mˆ  emoire que la fonction appelante´

? la fonction appelee´   modifie (indirectement) la valeur de plusieurs parametres` de la fonction appelante

? on parle de passage par copie d’adresse

avant appel : n=1 p=5

adresses avant appel : 0xbffbf524 0xbffbf520 debut echange : 1 5

adresses debut echange : 0xbffbf524 0xbffbf520 fin echange : 5 1

adresses fin echange : 0xbffbf524 0xbffbf520

apres appel : n=5 p=1

adresses après appel : 0xbffbf524 0xbffbf520

                    7.7.4          Le vrai echange : visualisation de la RAM´      a l’ex`         ecution´

Dans la fonction main avant l’appel a la fonction`  echange : int n=1; int p=5;

 

Attention : pour simplifier, chaque segment represent´  e a la taille d’un entier ou d’un´ pointeur sur un entier.

A l’appel de la fonction echange : passage par copie d’adresse

                     ? echange de´      *ptra et *ptrb

 

                     Au retour a la fonction`         main les valeurs de n et p ont bien et´ e´ echang´              ees :´



 
                    7.7.5         Bilan sur le passage de parametres`

Si l’on souhaite modifier une seule variable de la fonction appelante :

? privilegier le´      passage par copie de valeur des variables que l’on ne souhaite pas modifier

(conversions implicites possibles)

? utiliser la valeur de retour de la fonction pour modifier la variable concernee´

Si l’on souhaite modifier deux variables ou plus de la fonction appelante :

                    ? privilegier le´  passage par copie d’adresse des variables a modifier (pointeurs)`

                      (chaque pointeur doit etre du mˆ  eme type que la variable pointˆ                                        ee)´

? passage par valeur des variables que l’on ne souhaite pas modifier

? fonction avec ou sans retour selon les besoins

Remarque importante : dans le passage par copie d’adresse il faut respecter exactement le type (eviter la conversion de type de pointeur)´


7.8 Retour sur printf/scanf

              7.8       Retour sur printf/scanf

printf ne modifie pas les variables qu’il rec¸oit en argument

? passage par copie de valeur Dans la fonction appelante :

double x = 0.2 ;float y = 3.4f ;int n = 10 ; printf("x=%g, y=%g, n=%d\n", x, y, n);

(conversions implicites possibles)

                     scanf modifie la valeur des variables qu’il rec¸oit a partir du deuxi`                                                            eme argument`

? il est possible de modifier la valeur de plusieurs variables ? passage par copie de valeur du premier argument ? passage par copie d’adresse des arguments qui suivent Dans la fonction appelante :

double x ;float y ;int n ; scanf("%lg %g %d", &x, &y, &n);

               (attention : le pointeur doit etre du mˆ  eme type que la variable pointˆ                                         ee)´

                                                                                                                                                                                                                                     7.9 Complement sur les fonctions : la r´                                                                                                                                                                  ecursivit´                                                                                                                                                                              e´

              7.9         Complement sur les fonctions : la r´     ecursivit´ e´

                  Fonction recursive = fonction qui s’appelle elle-m´  emeˆ

              7.9.1          Exemple de fonction recursive : factorielle´

/* programme fct_recurs.c */

int fact(int n){ /* calcul recursif´                                     de factorielle */

/* attention aux depassements´  de capacite´ non testes´ en entier */ int factorielle;

if ( n > 0 ){

factorielle = n * fact(n-1); /* provoque un autre appel a` fact */

} else {

factorielle = 1 ; /* fin de la recursion */

}

return factorielle;

}

             8      Tableaux

              8.1      Definition et usage´

Un tableau est un ensemble ”rectangulaire” d’el´ ements :´

–    de meme typeˆ      ,

–    reper´ es au moyen d’´   indices entiers,

–    stockes en m´ emoire´     a des`        adresses contigues¨ .

L’ensemble de ces el´ ements est identifi´  e par un´   identifiant unique : le nom du tableau.

Les tableaux sont utilises lorsque l’on manipule plusieurs objets de m´       eme type,ˆ ayant un lien entre eux :

–    les differentes valeurs d’une fonction (vecteur de´ N points),

–    les coordonnees de plusieurs points´ (matrice de N points ×2 (ou 3) coordonnees)´ – etc


                                8.1.1            Exemples de programmes el´ ementaires utilisant des tableaux 1D´

/* fichier tableaux-elem1d_1.c */

#include <stdio.h> #include <stdlib.h>

int main(void){

int i;

// declaration + affectation d’un tableau de 3 reels (double) double tab1d[3] = {0.5, 1.5, 2.5}; // taille fixe de 3

           //                                      ˆ un seul indice: tableau 1D

// impression du tableau a l’aide d’une boucle for:

for (i=0; i<3; i++){ // attention: indice i varie de 0 a 2

printf("element %d du tableau = tab1d[%d] = %g\n", i+1, i, tab1d[i]);

}

exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

element 1 du tableau = tab1d[0] = 0.5 element 2 du tableau = tab1d[1] = 1.5 element 3 du tableau = tab1d[2] = 2.5

Caracteristiques de ce programme :´

–    affectation globale du tableau des sa d`         eclaration (initialisation)´

–    utilisation d’une boucle pour l’impression des el´ ements du tableau´

/* fichier tableaux-elem1d_2.c */

#include <stdio.h> #include <stdlib.h> int main(void){ int i;

// declaration d’un tableau de 3 reels (double):

double tab1d[3]; // taille fixe de 3

           //                                      ˆ un seul indice: tableau 1D

// affectation du tableau a l’aide d’une boucle for:

for (i=0; i<3; i++){ // attention: indice i varie de 0 a 2

tab1d[i] = (double) i + 0.5;

}

// impression du tableau a l’aide d’une boucle for:

for (i=0; i<3; i++){ // attention: indice i varie de 0 a 2

printf("element %d du tableau = tab1d[%d] = %g\n", i+1, i, tab1d[i]);

}

exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

element 1 du tableau = tab1d[0] = 0.5 element 2 du tableau = tab1d[1] = 1.5 element 3 du tableau = tab1d[2] = 2.5

Caracteristiques de ce programme :´

–    utilisation d’une boucle pour l’affectation du tableau

–    utilisation d’une boucle pour l’impression des el´ ements du tableau´

                                8.1.2            Exemples de programmes el´ ementaires utilisant des tableaux 2D´

/* fichier tableaux-elem2d_0.c */

#include <stdio.h> #include <stdlib.h> int main(void){ int i, j;

// declaration + affectation d’un tableau de 6 entiers int tab2d[2][3] = {{1,2,3},{4,5,6}}; // taille fixe de 6

                 //                                ˆ ˆ deux indices: tableau 2D

// impression du tableau a l’aide de deux boucles for: for (i=0; i<2; i++){ // attention: indice i varie de 0 a 1

for (j=0; j<3; j++){ // attention: indice i varie de 0 a 2

printf("tab2d[%d][%d] = %d\n", i, j, tab2d[i][j]);

} }

exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

tab2d[0][0] = 1 tab2d[0][1] = 2 tab2d[0][2] = 3 tab2d[1][0] = 4 tab2d[1][1] = 5 tab2d[1][2] = 6

/* fichier tableaux-elem2d_1.c */

#include <stdio.h> #include <stdlib.h> int main(void){ int i, j;

// declaration d’un tableau de 6 entiers int tab2d[2][3]; // taille fixe de 6

                 //                                ˆ ˆ deux indices: tableau 2D

// affectation du tableau

for (i=0; i<2; i++){ // attention: indice i varie de 0 a 1

for (j=0; j<3; j++){ // attention: indice i varie de 0 a 2

tab2d[i][j] = 3*i + j + 1;

}

}

// impression du tableau sous forme de matrice:

for (i=0; i<2; i++){ // attention: indice i varie de 0 a 1 for (j=0; j<3; j++){ // attention: indice i varie de 0 a 2 printf(" %d ", tab2d[i][j]);

} printf("\n"); } exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

1 2 3

4 5 6


8.2        Tableaux de taille fixe

8.2.1         Declaration d’un tableau de taille fixe´

–    declaration d’un tableau´      a` 1 indice (vecteur) de 3 reels´     (double) : double tab1d[3] ;

–    declaration d’un tableau´      a` 2 indices (matrice) de 6 entiers : int tab2d[2][3] ;

–    etc

La dimension d’un tableau correspond au nombre d’indices (ou de ”directions”) ? un tableau a 1 indice est un tableau 1D (un vecteur)`

? un tableau a 2 indices est un tableau 2D (une matrice)`

La taille d’un tableau correspond a l’espace occup`  e par les variables stock´ ees dans´ les el´ ements du tableau´

? un tableau 1D de 3 entiers a une taille de 3*sizeof(int)

? un tableau 2D de 6 doubles a une taille de 6*sizeof(double)

Cas gen´ eral :´ declaration d’un tableau de taille fixe´       a` n indices de n1 ? n2 ? ? nn variables :

type_tab nom_tab[n1][n2] [nn] ;

? dimension : n (on a n ”directions”)

? taille : n1 ? n2 ? ? nn?sizeof(type_tab)

–    type_tab est le type des variables stockees dans les´         el´ ements du tableau´        ,

–    nom_tab est l’identifiant du tableau,

–    ni est une constante entiere postitive correspondant au`     nombre d’el´ ements´ dans la i?eme direction`         .

Attention : la dimension d’un tableau peut faire ref´ erence :´

–    au nombre d’indices (ou directions) ? convention adoptee ici´   .

–    a la valeur d’un indice donn` e. Par exemple : 3 dans le cas de´ double tab1d[3] ;

8.2.2      Indexation et ref´ erence´  a un` el´ ement d’un tableau´

Pour un tableau a 1 indice, de`     n el´ ements´ , l’indice varie de 0 a` n-1.

Exemple : tab1d[2] est le troisieme`            el´ ement du tableau.´

Exemples de ref´ erences´    a des` el´ ements de tableaux :´

Tableau 1D de doubles : x = tab1d[2] ;

? on stocke le 3eme`   el´ ement de´   tab1d dans x

ou` x est un double

Tableau 2D d’entiers : i = tab2d[1][0] ;

ou` i est un entier

? on stocke le 4eme`   el´ ement de´   tab2d dans i

Attention : pas d’operateur s´   equentiel´                   est interpret´ e comme´

[0]

Attention : gen´         eralement, aucun contr´   ole sur la valeur de l’indice n’est effectuˆ    e par´ le compilateur

? en cas de debordement d’indice´ il y a un risque important d’erreurde segmentation (voir plus loin)

8.2.3           Declaration d’un tableau 1D : visualisation de la RAM´

Declarer´  un tableau ? reserver une zone m´ emoire´    pour stocker l’ensemble de ses el´ ements :´

–    la taille de cette zone depend du´ type du tableau et du nombre de ses el´ ements,´

–    l’emplacement des el´ ements du tableau en m´   emoire correspond´  a des`       adresses contigues¨ .

Exemple de declaration d’un tableau´      a 1 dimension :` double tab1d[3] ;

 

8.2.4        Affectation d’un tableau

On distingue deux manieres d’affecter un tableau de taille fixe :`

A la declaration :´     (initialisation)

– Pour un tableau a une dimension (vecteur) :`

double tab1d[3] = {0.5,1.5,2.5};

produit le tableau tab1d tel que tab1d[0]=0.5, tab1d[1]=1.5 et tab1d[2]=2.5.

–    Pour un tableau a deux dimensions (matrice) :`

int tab2d[2][3] = {{1,2,3},{4,5,6}} ;

produit le tableau tab2d tel que tab2d[0][0]=1, tab2d[0][1]=2, tab2d[1][0]=4, etc

Remarque : tableau 2d = tableau de tableaux

? cas de tab2d[2][3] : 1 tableau de 2 tableaux de 3 el´ ements de type´                                                                                                                                        int

? pas de veritable notion de tableau multidimensionnel en C.´

Hors de la declaration :´   (methode la plus courante´      ) il est alors impossible de preciser les valeurs d’un tableau en une seule instruction´ ? recours aux boucles en gen´ eral´

–    Pour le tableau tab1d prec´ edent :´ double tab1d[3] ;

for (i=0; i<3; i++) { /* indice variant de 0 a 2 */

tab1d[i] = i + 0.5 ;

}

–    Pour le tableau tab2d prec´ edent :´ int tab2d[2][3] ;

for (i=0; i<2; i++) {                                                       /* indice lent */

                                                for (j=0; j<3; j++) {                          /* indice rapide */

tab2d[i][j] = 3*i + j + 1 ;

} }

8.2.5            Affectation d’un tableau 1D : visualisation de la RAM

Exemple d’initialisation d’un tableau :

double tab1d[3] = {0.5,1.5,2.5} ;

     double  tab1d[3]  = { 0.5, 1.5, 2.5 }; 

 

Attention : en cas de debordement d’indice´           il y a un risque important

d’erreur de segmentation

8.2.6            Affectation d’un tableau 2D : visualisation de la RAM

Exemple d’initialisation d’un tableau 2D :

int tab[2][3] = {{1,2,3},

{4,5,6}};

 

Remarque importante :

Dans le cas de tableaux a plusieurs dimensions` l’indice des el´ ements contigus´       est l’indice le plus a droite`    .

8.2.7         Ordre des el´ ements de tableaux 2D en C´

Dans le cas de tableaux a plusieurs dimensions` l’indice qui defile le plus vite´  est celui des el´ ements contigus´ ? c’est donc l’indice le plus a droite`      .

Ainsi, dans le cas d’un tableau 2D :

–    1er indice = ”lignes” de la matrice

–    2eme indice (le plus rapide) = ”colonnes” de la matrice`

Consequence :´ la boucle la plus interne est sur l’indice rapide ? augmente la vitesse de calcul

(un bon compilateur optimise automatiquement le code de cette maniere)`


                           8.3         Inconvenients des tableaux de taille fixe´

Tres fr`          equemment, on est amen´         e´ a ex`   ecuter un m´     eme programme sur un ensembleˆ d’el´ ements dont´        le nombre varie d’une execution´   a l’autre`

(exemple : la resolution avec laquelle on examine une fonction)´

? l’utilisation des tableaux de taille fixe (tel que ci-dessus) necessite une´ modification de nombreuses lignes de code (risque d’erreurs)

                           On etudiera trois mani´ eres de rem` edier´ a cet inconv` enient :´

–    l’utilisation d’une directive preprocesseur´

–    l’utilisation de tableaux automatiques,

–    l’allocation dynamique (prochain chapitre)

                           8.3.1         Directive preprocesseur (ancienne m´    ethode)´

                              Utiliser une directive du preprocesseur pour´   parametrer la taille´                                                           :

#define N 20 (noter l’utilisation de la majuscule)

int main(void) { int i, j ;

int tab2d[N][2*N+1] ; for (i=0; i<N; i++) { for (j=0; j<2*N+1; i++) {

Avantage : changer la taille du tableau ne necessite que la modification de la 1´         ere` ligne.

                            Inconvenient :´                    changer la taille du tableau necessite une recompilation.´

/* programme tableaux-elem2d_2.c */

#include <stdio.h> #include <stdlib.h>
#define LIGNES 3 // taille dans la 1ere direction #define COLONNES 5 // taille dans la 2eme direction

int main(void) { int i, j;

// declaration d’un tableau de 15 elements: int t[LIGNES][COLONNES]; // taille fixe de 15

// affectation du tableau:

                                 for (i=0; i<LIGNES; i++) {                                     // indice lent

                                         for (j=0; j<COLONNES; j++) {                        // indice rapide

t[i][j] = 10*i + j + 11 ;

}

}

printf("impression du tableau 2D t[i][j]=10*i+j+11\n"); printf("en faisant varier j à i fixé\n");

for (i=0; i<LIGNES; i++) {   // indice lent for (j=0; j<COLONNES; j++) { // indice rapide printf("%d ", t[i][j]) ; } printf("\n") ; } exit(0) ;

}

                           Resultat´  a l’ex` ecution :´

impression du tableau 2d t[i][j]=10*i+j+11 en faisant varier j à i fixé

11 12 13 14 15

21 22 23 24 25

31 32 33 34 35

                           8.3.2         Declaration tardive et tableau automatique (m´      ethode conseill´                                       ee)´

                                En C99, on peut profiter de la possibilite offerte par les´                                                  declarations tardives´                                                  :

int n ;

scanf("%d", &n) ; /* on demande à l’utilisateur la taille */ double tab[n] ;

                              Le tableau tab est declar´  e´ APRES une instruction ? declaration tardive.´

Avantage :

Le code est compile une fois pour toutes.´

                             La valeur de n est modifiee´   a l’ex`             ecution seulement (taille ”variable”).´

Remarque importante : Il faut affecter n AVANT de declarer le tableau.´ Le code suivant compile, mais a de grandes chances de provoquer une erreur : int n ;

double tab[n] ; /* la valeur de n est indéfinie ici !!*/

                           8.3.3        Exemple de programme utilisant un tableau automatique

/* programme declar-tardive.c */

#include <stdio.h>

#include <stdlib.h>

//--------- attention: norme C99 -------------------------int main(void) {

int n; // taille du tableau 1D (vecteur)

printf("Entrer la taille du vecteur\n"); scanf("%d", &n); // taille fixee par l’utilisateur /* tableau de taille variable => decl. tardive on peut utiliser tab partout sous sa declaration: */ double tab[n];

printf("*** Saisie des elements ***\n");

for (int i=0; i<n; i++) { // decl. tardive de i locale au sous-bloc printf("tab[%d] ? ", i); scanf("%lg", &tab[i]);

} // sortie du sous-bloc: la zone memoire occupee par i est liberee printf("*** Affichage des elements ***\n");

for (int i=0; i<n; i++) { // decl. tardive de i locale au sous-bloc // i est une nouvelle variable sans lien avec la prec´ edente´ printf("tab[%d] = %g\n", i, tab[i]);

}

exit(EXIT_SUCCESS);

}

Entrer la taille du vecteur

3

*** Saisie des elements *** tab[0] ? tab[1] ? tab[2] ?

*** Affichage des elements *** tab[0] = 2.5 tab[1] = -4.45 tab[2] = 0.123


                           8.4        Tableaux et pointeurs en C

                           8.4.1       Notions de base

– un tableau est un pointeur constant sur le premier el´ ement du tableau´    , – la valeur de ce pointeur est l’adresse du premier el´ ement du tableau´       .

Exemple : float tab[9] ;

? tab est un pointeur vers tab[0]

? tab correspond a` &tab[0]

? *tab correspond a` tab[0]

L’affectation globale tab = est donc impossible

Application :

scanf("%g", tab); /* equivalente a: scanf("%g", &tab[0]); */

? conversion de tab en un pointeur constant vers tab[0]

                           8.4.2       Arithmetique des pointeurs´

                             On peut faire des operations arithm´                        etiques (addition, soustraction) sur des pointeurs´

                             ? cela revient a se d`  eplacer dans la m´ emoire.´

Exemple dans le cas d’un tableau : float tab[9] ;

                                     ? tab + i                         correspond a` &tab[i]

? *(tab + i) correspond a` tab[i]

Exemple dans le cas d’un pointeur ordinaire : float *pf ;

pf = &tab[3] ; /* equivalente a: pf = tab + 3 ; */ si pf correspond a` &tab[3] ? pf++ correspond a` &tab[4] si pf correspond a` &tab[3] ? pf-- correspond a` &tab[2]

                              ? pf sous-tableau commenc¸ant au 4e el´ ement de´     tab

(attention : on peut revenir en arriere)`

                           8.4.3          Retour sur l’ordre des el´ ements de tableaux 2D en C´

/* programme tableaux-elem2d_3.c */

#include <stdio.h> #include <stdlib.h>

#define LIGNES 3

// taille dans la 1ere direction

#define COLONNES 5

// taille dans la 2eme direction

int main(void) {

int i, j;

int t[LIGNES][COLONNES]; // taille fixe de 15 int *k = &t[0][0]; // pointeur sur t[0][0] // affectation du tableau:

                                 for (i=0; i<LIGNES; i++) {                                     // indice lent

                                         for (j=0; j<COLONNES; j++) {                        // indice rapide

t[i][j] = 10*i + j + 11 ;

} } printf("impression du tableau 2D t[i][j]=10*i+j+11\n"); printf("en faisant varier j à i fixé\n");

for (i=0; i<LIGNES; i++) {   // indice lent for (j=0; j<COLONNES; j++) {  // indice rapide printf("%d ", t[i][j]) ; } printf("\n") ;

}

printf("impression du tableau 2D t[i][j]=10*i+j+11\n"); printf("en suivant l’ordre en memoire\n"); for (i=0; i<LIGNES*COLONNES; i++) {

printf("%d ", *(k+i)) ; // ou meme k[i] } printf("\n") ;

printf("=> l’indice le plus a droite varie le plus vite\n") ; exit(0) ;

}

                           Resultat´  a l’ex` ecution :´

impression du tableau 2d t[i][j]=10*i+j+11 en faisant varier j à i fixé

11 12 13 14 15 21 22 23 24 25

31 32 33 34 35

impression du tableau 2d t[i][j]=10*i+j+11 en suivant l’ordre en mémoire

11 12 13 14 15 21 22 23 24 25 31 32 33 34 35

                              ? l’indice le plus a droite`     varie le plus vite

                           8.4.4         Sous-tableau 1D avec un pointeur

/* programme sous-tab1d.c */

#include <stdio.h>

#include <stdlib.h>

#define N 10 // taille du tableau 1D

/* manipulation d’un sous-tableau 1d avec un pointeur */ int main(void) {

double tab[N]; // declaration du tableau 1D initial double *ptrd=NULL; // pointeur sur le memeˆ type

                                                                                                                                            // que les el´ emts´        du tableau

int i;

for (i = 0 ; i < N ; i++) {

tab[i] = (double) i ; // remplissage du tableau

}

                                                 ptrd = tab + 3; // equivaut´              a` ptrd=&tab[3]

// affichage du tableau et du sous tableau

for (i = 0 ; i < N ; i++) {

printf(" tab[%d] = %g", i, tab[i]);

if (i < N - 3 ) { // au dela,` sortie du tableau initial printf(", ptrd[%d] = %g", i, ptrd[i]);

} printf("\n") ;

}

// on peut meme revenir en arriere

printf("ptrd[-1]=%g\n",ptrd[-1]);

exit(0) ;

}

                           Resultat´  a l’ex` ecution :´

tab[0] = 0, ptrd[0] = 3 tab[1] = 1, ptrd[1] = 4 tab[2] = 2, ptrd[2] = 5 tab[3] = 3, ptrd[3] = 6 tab[4] = 4, ptrd[4] = 7 tab[5] = 5, ptrd[5] = 8 tab[6] = 6, ptrd[6] = 9 tab[7] = 7 tab[8] = 8 tab[9] = 9

ptrd[-1]=2

                           8.4.5        Tableaux 2D et pointeurs

                                    Un tableau 2D correspond a un tableau de tableau`       :

int tab2d[2][3] = {{1,2,3},{4,5,6}} ;

                               Un tableau 2D correspond donc a un`      pointeur de pointeur :

–    tab2d est un pointeur sur un pointeur d’entier.

Sa valeur constante est l’adresse du premier el´ ement du tableau :´ tab2d correspond a` &tab2d[0]

–    tab2d[i] pour i=0,1 est aussi un pointeur constant.

Sa valeur constante est l’adresse du premier el´ ement de la ligne´ i du tableau : tab2d[i] correspond a` *(tab2d+i) et donc a` &tab2d[i][0].

Donc : *(*(tab2d+i)+j) correspond a` tab2d[i][j].

/* fichier tab2d_defer.c */

#include <stdio.h>

#include <stdlib.h>

#define N 3 // taille fixe dans la 1ere direction #define P 4 // taille fixe dans la 2eme direction int main(void) { int tab2[N][P]; // tableau 2D de taille 12

for (int i=0; i < N; i++) {

for (int j=0; j < P; j++) { tab2[i][j]=i*j; printf("%d ", tab2[i][j]);

} printf("\n");

}

// affichage de tab2[1][2] de 3 manieres:

printf("%d\n",tab2[1][2]); printf("%d\n",(*(tab2+1))[2]); printf("%d\n",*(*(tab2+1)+2)); exit(0);

}

                           Resultat´  a l’ex` ecution :´

0 0 0 0

0 1 2 3

0 2 4 6

2

2

2

                           8.4.6         Utilisation de typedef

typedef permet de definir des synonymes de types en C´

Recette syntaxique : dans une declaration classique, remplacer le nom de la´ variable par le synonyme et inserer´ typedef en teteˆ

                                Exemple 1 : choisir les types flottants de fac¸on parametr´   ee´

typedef float real; ou typedef double real; puis,

real x, y;

Exemple 2 : syntaxe plus delicate avec les tableaux´ typedef float vect[3]; puis

vect u, v;

2 tableaux de 3 float

vect tv[100];

tv tableau de 100 tableaux de 3 float

                                                                                                                                                                                equivaut´    a` float tv[100][3];


                           8.5        Fonctions et tableaux

                           8.5.1            Passage de tableaux de taille fixe (pour le compilateur)

Passage de tableau : transmission d’un tableau d’une fonction a une autre.`

Dans le cas d’un tableau de taille fixe, cette taille est une expression constante

–    qui est soit codee en dur au sein du programme (´   a` eviter´  ),

–    soit specifi´       ee par une directive´ #define du preprocesseur (´        pref´ erable´     )

                           Un tableau etant un´ pointeur sur le premier el´ ement du tableau :´

? passage par copie d’adresse (celle du premier el´ ement)´

? les valeurs stockees dans le tableau peuvent´ etreˆ   modifiees´        de la fonction appelante vers la fonction appelee et vice versa.´

                            Inconvenient :´     changer la taille necessite une recompilation´

                           8.5.2           Exemple de passage d’un tableau 1D de taille fixe

/* fichier C99/tab1d-fixe.c */

#include <stdio.h>

#include <stdlib.h>

#define N 4 // taille fixe du tableau

void init_et_print(int t1d[N]); // prototype de la fonction void init_et_print(int t1d[N]) { // definition de la fonction

//       tableau 1D ˆˆˆˆˆˆ : pointeur for (int i=0; i < N; i++) {

                                        t1d[i] = i - 2;                                                  // affectation

                                          printf("%2d ",t1d[i]);                                // affichage

} printf("\n");

return; // fonction sans retour

}

int main(void) {

int tab1d[N]; // taille fixe de N=4

init_et_print(tab1d); // passage par copie d’adresse

            //                                tab1d ˆˆˆˆˆ est equivalent a &tab1d[0]

// le tableau a effectivement ete modifie dans le main: for (int i=0; i < N; i++) {

printf("dans main : tab1d[%d]=%d\n", i, tab1d[i]);

}

exit(EXIT_SUCCESS);

}

                           Resultat´  a l’ex` ecution :´

-2 -1 0 1

dans main : tab1d[0]=-2 dans main : tab1d[1]=-1 dans main : tab1d[2]=0 dans main : tab1d[3]=1

                           8.5.3           Exemple de passage d’un tableau 2D de taille fixe

/* fichier C99/tab2d-fixe.c */

#include <stdio.h>

#include <stdlib.h>

#define N 4 // taille fixe dans la premiere direction #define P 8 // taille fixe dans la deuxieme direction void init_et_print(int t2d[N][P]); // prototype de la fonction void init_et_print(int t2d[N][P]) { // definition de la fonction // tableau 2D ˆˆˆˆˆˆˆˆˆ : pointeur de pointeur for (int i=0; i < N; i++) {

for (int j=0; j < P; j++) {

t2d[i][j]=i-j// affectation printf("%2d ",t2d[i][j]); // affichage

} printf("\n");

}

return; // fonction sans retour

}

int main(void) {

int tab2d[N][P]; // taille fixe de NxP=32

init_et_print(tab2d); // passage par copie d’adresse

// tab2d ˆˆˆˆˆ est equivalent a &tab2d[0] // le tableau a effectivement ete modifie dans le main: printf("dans main : tab2d[0][4]=%d\n",tab2d[0][4]); exit(EXIT_SUCCESS);

}

                           Resultat´  a l’ex` ecution :´

0      -1 -2 -3 -4 -5 -6 -7

1      0 -1 -2 -3 -4 -5 -6

2      1 0 -1 -2 -3 -4 -53 2 1 0 -1 -2 -3 -4

dans main : tab2d[0][4]=-4

                           8.5.4           Passage de tableau de taille inconnue a la compilation`

Taille inconnue ? utiliser les tableaux automatiques

                            ? la taille peut etre choisieˆ  a l’ex` ecution du programme´

Le passage se fait toujours par copie d’adresse

? modification possible du tableau dans la fonction appelante.

Attention a la syntaxe`         : dans la liste des arguments de la fonction, il faut declarer la taille du tableau´         AVANT le tableau lui-meme.ˆ

Exemple : cas d’un tableau 2D la fonction doit avoir un entete du type :ˆ

void init_et_print(int n, int p, int t[n][p])

                           ? declarer´      n et p avant t dans les arguments de la fonction

                           8.5.5            Exemple de passage d’un tableau 1D de taille variable

/* fichier C99/tab1d-var.c */

#include <stdio.h>

#include <stdlib.h>

//--------- attention: norme C99 ---------------------------

// passage en argument d’un tableau 1d de taille variable

// => il faut aussi passer la taille du tableau a` la fct // => il ft declarer´     la taille du tabl. AVANT le tableau void init_et_print(const int n, int t[n]);

// ATTENTION:  ˆˆˆˆˆ n       non modifiable dans la fonction void init_et_print(const int n, int t[n]) { for (int i=0; i < n; i++) {

                               t[i] = i - 2;                                             // affectation

                                 printf("%2d ",t[i]);                           // affichage

} printf("\n");

return; // fonction sans retour

}

int main(void) {

int n;

printf("Entrer n: "); scanf("%d", &n);

int tab[n]; // tableau de taille variable, definie par l’utilisateur init_et_print(n,tab); // passage par copie d’adresse de tab printf("dans main : tab[n-1]=%d\n",tab[n-1]); exit(EXIT_SUCCESS);

}

Entrer n: 4

-2 -1 0 1

dans main : tab[n-1]=1

                           8.5.6            Exemple de passage d’un tableau 2D de taille variable

/* fichier C99/tab2d-var.c */

#include <stdio.h>

#include <stdlib.h>

//--------- attention: norme C99 ---------------------------

// passage en argument d’un tableau 2d de taille variable

// => il faut aussi passer la taille du tableau a` la fct // => il ft declarer´     la taille du tabl. AVANT le tableau void init_et_print(const int n, const int p, int t[n][p]);

// ATTENTION:  ˆˆˆˆˆ n       et ˆˆˆˆˆ p non modifiables dans la fonction void init_et_print(const int n, const int p, int t[n][p]) {

for (int i=0; i < n; i++) {

for (int j=0; j < p; j++) {

                                      t[i][j]=i-j;                                            // affectation

printf("%2d ",t[i][j]); // affichage

} printf("\n");

}

return; // fonction sans retour

}

int main(void) {

int n, p;

printf("Entrer n et p: "); scanf("%d %d", &n, &p);

int tab[n][p]; // taille variable, definie par l’utilisateur init_et_print(n,p,tab); // passage par copie d’adresse de tab printf("dans main : tab[n-1][p-1]=%d\n",tab[n-1][p-1]); exit(EXIT_SUCCESS);

}

Entrer n et p: 2 8

0 -1 -2 -3 -4 -5 -6 -7 1 0 -1 -2 -3 -4 -5 -6

dans main : tab[n-1][p-1]=-6

                           8.5.7         Limite des tableaux automatiques

Les tableaux automatiques sont la solution a utiliser dans la plupart des cas`

Comme toutes les variables consider´ ees jusqu’ici, ces tableaux sont visibles dans le´ programme :

–    apres leur d`    eclaration et au sein du´   bloc dans lequel ils sont declar´     es´ ,

–    dans les fonctions appelees´   dans ce bloc, pourvu qu’ils soient passes comme´ argument.

Limitation :

les tableaux automatiques, s’ils sont declar´          es dans une fonction, ne sont´         pas visibles dans la fonction appelante.

Remarque :

En fait, ces tableaux sont declar´  es sur la´   pile  : c’est-a-dire la partie de la` memoire g´   er´ ee´       automatiquement par l’ordinateur.

/* fichier C99/tab-pile2.c */

#include <stdio.h>

#include <stdlib.h>

// -------------- attention: norme C99 ------// limite des tableaux automatiques:

// declares dans une fonction ils ne sont PAS // visibles dans la fonction appelante void print_1D(const int n, const int t[n]);

//                             n ˆˆˆˆˆ                       et t ˆˆˆˆˆ non modifiables

int * retour_tab(const int nn);

// ˆ retourne un pointeur de int (tableau 1D de int)

void print_1D(const int n, const int t[n]) {

printf("impression du tableau dans print_1d:\n"); for (int i = 0; i < n; i++) {

printf("%d ", t[i]) ; // pas de modification de t

} printf("\n"); return; // fonction sans retour

}

int * retour_tab(const int nn){ int tab[nn];         // declaration locale de tab for (int i=0; i<nn; i++) {

tab[i] = i; // affectation de tab

}

print_1D(nn, tab);

return tab; // fonction retournant un tableau de int

}

int main(void) {

int n, *t=NULL; // declaration de t sans initialisation printf("entrer la taille n du tableau: \n"); scanf("%d", &n);

t = retour_tab(n); // appel de la fonction tab_var

printf("impression du tableau dans le main:\n"); for (int i = 0; i < n; i++) { printf("%d ", t[i]) ; // retourne n’importe quoi !

} exit(0) ;

}

Avertissement a la compilation :`

tab-pile2.c: In function ’retour_tab’:

tab-pile2.c:23: attention : cette fonction retourne l’adresse d’une variable locale

                            Probleme (al` eatoire)´ a l’ex` ecution :´

entrer la taille n du tableau: 5 impression du tableau dans print_1d:

0 1 2 3 4

impression du tableau dans le main: -1228844832 32767 438773941 62 4

Ce type de probleme survient aussi dans le cas suivant :`

–    un fichier de donnees contient la taille d’un tableau et les valeurs de ses´ el´ ements,´

–    on veut ecrire une fonction qui lit ce fichier,´   alloue le tableau et le remplit, – cette fonction est appelee (par exemple) dans´         main.

Probleme :` impossible d’utiliser le tableau dans le main puisqu’il n’y est pas visible.

Solution : utiliser un tableau dynamique ? allocation dynamique.

Remarque :

Les tableaux dynamiques sont declar´  es sur le´   tas  : c’est-a-dire la partie de la` memoire g´   er´ ee´       manuellement par le programmeur. Le tas permet d’assurer une visibilite´ des variables.


9          Allocation dynamique (sur le tas)

9.1      Motivation

Du point de vue de l’allocation de memoire, on distingue trois types de tableaux :´ – tableaux statiques : occupent un emplacement bien defini avant ex´   ecution´

–    tableaux automatiques : pas d’emplacement defini avant ex´       ecution´

        ? alloues/d´ esallou´ es ”automatiquement” (sur la´             pile (stack en anglais))

–    tableaux dynamiques : pas d’emplacement defini avant ex´ ecution´

        ? alloues/d´ esallou´ es ”manuellement” (sur le´     tas (heap en anglais))

Avantage des tableaux dynamiques :

–    leur taille peut varier pendant l’execution du programme comme pour les tableaux´ automatiques,

–    les tableaux dynamiques peuvent etre allouˆ         es dans une fonction et retourn´        es´ dans une autre contrairement aux tableaux automatiques.

C’est au programmeur de se charger de l’allocation et de la liberation de la´ memoire dynamique´

9.2 Allocation dynamique avec malloc ou calloc

9.2               Allocation dynamique avec malloc ou calloc

Deux fonctions standard permettent d’allouer un espace memoire sur le tas.´

Leur prototype est dans le fichier : stdlib.h

? directive preprocesseur (compilation) :´                #include <stdlib.h>

Prototype de malloc : void *malloc(size_t taille) ;

–    Un argument taille : nombre d’octets a allouer.`

–    Une valeur de retour du type void * (pointeur sur void) :

–    l’adresse de l’emplacement alloue si tout se passe bien,´

–    le pointeur NULL en cas de probleme.`

Exemple : allocation d’un tableau 1D de 10 doubles

double *ptr = NULL ;

ptr = (double *) malloc(10*sizeof(double)) ;

Le pointeur peut etre ensuite utilisˆ    e avec le formalisme tableau´

(ptr[0] ptr[9])

Noter : conversion de void * en double * et utilisation de sizeof

9.2 Allocation dynamique avec malloc ou calloc

Prototype de calloc :

void *calloc(size_t nb_bloc, size_t taille) ;

–    Deux arguments taille : nombre d’octets a allouer.`

–    nb_bloc : nombre de blocs consecutifs de´     taille octets a allouer,` – taille : nombre d’octets par bloc.

–    Une valeur de retour du type void * (pointeur sur void) :

–    l’adresse de l’emplacement alloue si tout se passe bien,´

–    le pointeur NULL en cas de probleme.`

La fonction calloc initialise tous les octets alloues´             a z` ero binaire´

(OK pour les entiers, probleme possible pour les r`       eels)´

Exemple : allocation d’un tableau 1D de 10 doubles

double *ptr = NULL ;

ptr = (double *) calloc(10, sizeof(double)) ;

                                                                                                                                                                                            9.3 Liberation de la m´                                                                                                                                                         emoire allou´                                                                                                                                                             ee avec´                                                                                                                                                                free

9.3       Liberation de la m´  emoire allou´ ee avec´    free

La gestion de la memoire dynamique (le´            tas ) est manuelle

? la liberation de l’espace allou´    e est´  a faire par le programmeur.`

La fonction standard free permet de liberer la m´         emoire allou´            ee dynamiquement.´

(son prototype est dans le fichier : stdlib.h)

Prototype de free : void free(void *adr) ;

– Un argument adr : adresse de l’emplacement a lib`         erer,´

(adr est un pointeur qui aura et´ e initialis´  e par´ malloc ou calloc) – Aucune valeur de retour.

Exemple : allocation puis liberation d’un tableau 1D de 10 doubles´ double *ptr = NULL ; ptr = (double *) calloc(10, sizeof(double)) ;

free(ptr) ;

ptr = NULL ; (pour plus de securit´                     e)´

9.4 Attention aux fuites de memoire´

9.4        Attention aux fuites de memoire´

int *adr = NULL ; n = 1 ;

adr = (int *) calloc(100, sizeof(int)) ; adr = &n ; /* fuite de memoire */

Plus aucun pointeur ne pointe vers l’espace alloue par´ calloc !

? cette partie de la memoire est perdue pour le restant du programme,´ il est impossible d’y acceder´ a nouveau.`

(il aurait fallu liberer la m´  emoire allou´ ee avant de r´ eaffecter´  adr)

? particulierement grave dans les boucles.`

Des fuites de memoires massives peuvent provoquer un plantage du programme.´

Remarque : certains langages (Java, mais pas C) ont des mecanismes de´ garbage collector pour detecter ces espaces perdus.´

9.5 Exemple d’allocation dynamique d’un tableau 1D

9.5           Exemple d’allocation dynamique d’un tableau 1D

/* fichier alloc-tab1d2.c */

#include <stdio.h> #include <stdlib.h>

/* ----- allocation dynamique d’un tableau 1D ---- */ int * alloc_et_init(const int n) { int *p=NULL; // pointeur sur entier local => NULL p=(int *)calloc(n, sizeof(int)); // affectation pointeur if (p == NULL) { // si l’affectation ne marche pas printf("Erreur d’allocation\n");

return p; // retour dans main avec le pointeur nul

}

for (int i=0; i < n; i++) {

p[i]=i; // pointeur sur entier = tableau 1D de int

}

return p; // fonction retourne le pointeur (tableau 1D)

}

9.5 Exemple d’allocation dynamique d’un tableau 1D

int main(void) { int n, *pti = NULL; // pointeur sur entier local => NULL printf("Entrer n "); scanf("%d",&n);

pti = alloc_et_init(n); // affectation par retour fonction if (pti != NULL) { // si l’allocation marche

for (int i=0; i < n; i++) {

printf("%d ",pti[i]);// affichage du tableau 1D

} } printf("\n");

free(pti); // liberation´  de l’espace pti=NULL; // securit´ e´ exit(0);

}

Entrer n 6

0 1 2 3 4 5


                           9.6      Exemple d’allocation dynamique d’un tableau 2D

Tableau 2D en C ? pointeur de pointeur

 

/* fichier alloc-tab2d.c */

#include <stdio.h> #include <stdlib.h> int ** alloc2d(const int n, const int p) { int *pespace=NULL, **ptab=NULL;

// 1) Allocation de l’espace pour la matrice pespace=(int *)calloc(n*p, sizeof(int)); if (pespace == NULL) { // pb allocation

return ptab;

}

// 2) Allocation du vecteur de pointeurs ptab=(int **)calloc(n,sizeof(int *)); if (ptab == NULL) { // pb allocation

return ptab;

}

// 3) Affectation au debut de chaque ligne for (int i=0; i < n; i++) {

ptab[i]=&pespace[i*p];

// ou pespace + i*p

}

                                         return ptab; // !! ptab est declar´                  e´ sur le tas

}

int main(void) { int n, p, **pti = NULL; /* initialisation a` NULL */

printf("Entrer n et p "); scanf("%d %d",&n, &p); pti=alloc2d(n, p); if (pti != NULL) { // allocation OK for (int i=0; i < n; i++) {

for (int j=0; j < p; j++) { pti[i][j]=i*j; printf("%d ",pti[i][j]); } printf("\n");

} }

free(pti[0]); // liberation´     de la matrice free(pti); // liberation´  du vecteur pti=NULL; // securit´    e´ exit(0);

}

Entrer n et p 3 6

0 0 0 0 0 0

0 1 2 3 4 5

0 2 4 6 8 10


                           9.7      La bibliotheque`      libmnitab

                                    Dans le cas des tableaux sur le tas : utiliser la bibliotheque`                                                                   libmnitab

                            ? directive preprocesseur´            #include "mnitab.h"

? compilation avec gcc+mni ou gcc+mni-c99

                             ? edition des liens avec´       -lmnitab

Cette bibliotheque contient de nombreuses fonctions permettant de g`  erer la´ memoire dynamique.´

Exemples :

–    Allocation et liberation de tableau 1D de doubles :´

double *double1d(int n) pour allouer l’espace et void double1d_libere(double *vec) pour liberer l’espace´

–    Allocation et liberation de tableaux 2D de floats :´

float **float2d(int n, int p) pour allouer l’espace et void float2d_libere(float **mat) pour liberer l’espace´ – d’autres fonctions de calcul de min ou de max

                           9.7.1           Exemple de passage d’un tableau dynamique 1D

/* fichier C99/tab1d-dyn.c */

#include <stdio.h> #include <stdlib.h>

#include "mnitab.h" // utilisation de la bibliotheque libmnitab

// passage en argument d’un tableau 1d dynamique // => il faut aussi passer la taille du tableau a` la fct void init_et_print(int n, double *t); void init_et_print(int n, double *t) {

for (int i=0; i < n; i++) { t[i] = (double) 1/(i + 1.);   // affectation printf("%4g ",t[i]); // affichage } printf("\n");

return; // fonction sans retour

}

int main(void) { int n;

double *tab=NULL;

// declaration d’un pointeur sur un double

printf("Entrer n:

");

scanf("%d", &n); tab = double1d(n); // allocation dynamique au moyen de la bibliotheque init_et_print(n,tab); // passage par copie d’adresse de tab printf("dans main : tab[n-1]=%g\n",tab[n-1]); double1d_libere(tab); // liberation de l’espace memoire tab = NULL; // par precaution

exit(EXIT_SUCCESS);

}

Entrer n: 4

1 0.5 0.333333 0.25 dans main : tab[n-1]=0.25

                           9.7.2           Exemple de passage d’un tableau dynamique 2D

/* fichier C99/tab2d-dyn.c */

#include <stdio.h> #include <stdlib.h>

#include "mnitab.h" // utilisation de la bibliotheque libmnitab

// passage en argument d’un tableau 2d dynamique // => il faut aussi passer la taille du tableau a` la fct void init_et_print(int n, int p, double **t); void init_et_print(int n, int p, double **t) {

for (int i=0; i < n; i++) { for (int j=0; j < p; j++) { t[i][j]=(double) 1./(i+j+1.);   // affectation printf("%10g ",t[i][j]);     // affichage } printf("\n");

}

return; // fonction sans retour

}

int main(void) {

int n, p;

double **tab=NULL; // declaration d’un double pointeur sur un double printf("Entrer n et p: "); scanf("%d %d", &n, &p);

tab = double2d(n,p); // allocation dynamique d’un tableau 2d de doubles init_et_print(n,p,tab); // passage par copie d’adresse de tab printf("dans main : tab[n-1][p-1]=%g\n",tab[n-1][p-1]); double2d_libere(tab); // liberation de la memoire allouee tab = NULL; // dissociation du pointeur exit(EXIT_SUCCESS);

}

Entrer n et p: 3 4

                                                                           1                       0.5           0.333333            0.25

                                                                   0.5           0.333333                       0.25              0.2

                                         0.333333                       0.25                        0.2  0.166667

dans main : tab[n-1][p-1]=0.166667


9.8 Bilan sur l’allocation de memoire´

                                9.8        Bilan sur l’allocation de memoire´

Quand utiliser l’allocation automatique (sur la pile) ?

Si le tableau est utilise :´

–    dans la fonction ou il est d`   eclar´         e,´

–    ou dans une fonction appelee par celle-ci,´

                                   ? pref´ erer les tableaux de taille variables sur la pile (´            int tab[n][p])

Quand utiliser l’allocation dynamique (sur le tas) ?

On peut TOUJOURS utiliser des tableaux dynamiques.

C’est indispensable si le tableau est utilise dans la fonction appelant la fonction o´         u` le tableau est defini´

? utiliser les allocations dynamiques (int **tab)

                                     Attention : les deux types de declarations´                   ne sont pas equivalentes´

? ne pas melanger les syntaxes.´

                                     Pour les tableaux dynamiques : penser a utiliser la biblioth`                                                           eque` libmnitab.

                                10       Chaˆ?nes de caracteres`

                                10.1       Definition et usage´

En C, il n’y a pas de type  chaˆ?ne de caracteres`  ? on utilise des tableaux de caracteres` qui se terminent par le caractere fin de cha` ˆ?ne : \0.

                                 Ces tableaux peuvent etreˆ                    statiques, automatiques ou dynamiques.

Il existe une bibliotheque standard` contenant des fonctions permettant de manipuler les tableaux de caracteres.`

                                      Dans la pratique, les chaˆ?nes de caracteres les plus r`  epandues sont :´

–    le premier argument des fonctions printf et scanf,

–    les noms des fichiers manipules par le programme,´ – etc

                                10.2   Exemple de tableaux de caracteres de taille fixe`

/* fichier char-elem_stat.c */

#include <stdio.h> #include <stdlib.h>

#include <string.h> // contient le prototype de strlen, etc

#define N 5

// --------- tableaux de caracteres de taille fixe ------int main(void){

// declaration sans affectation:

char cfixe1[N]; // tableau de N-1 caracteres + \0 // Declaration avec affectation:

char cfixe2[N]={’O’,’u’,’i’,’\0’}; // doit inclure \0 // Declaration avec affectation (methode conseillee):

char cfixe3[N]= "Non"; // sans preciser le \0


// Affectation du premier tableau a l’aide d’une boucle: for(int i=0;i<N-1;i++){       // attention: indice de 0 a N-2

cfixe1[i] = ’z’;

printf("%c", cfixe1[i]); // format %c pour un seul caractere

}

// Inclusion du caractere de fin de chaine (\0) - imperatif:

cfixe1[N-1] = ’\0’;

// Affichage de la taille (sans \0) a l’aide de la fonction strlen: printf("\nLongueur de la 1ere chaine: %d\n", (int) strlen(cfixe1)); printf("Longueur de la 2eme chaine:   %d\n", (int) strlen(cfixe2)); printf("Longueur de la 3eme chaine:   %d\n", (int) strlen(cfixe3)); // Affichage des tableaux de caracteres (format %s pour une chaine):

printf("Affichage du 1er tableau: %s\n", cfixe1); printf("Affichage du 2eme tableau: %s\n", cfixe2); printf("Affichage du 3eme tableau: %s\n", cfixe3); exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

zzzz

Longueur de la 1ere chaine: 4 Longueur de la 2eme chaine: 3 Longueur de la 3eme chaine: 3

Affichage du 1er tableau: zzzz

Affichage du 2eme tableau: Oui Affichage du 3eme tableau: Non

                                10.3            Exemple de tableaux de caracteres de taille quelconque`

#include <stdio.h> #include <stdlib.h>

#include <string.h> // contient prototype de strlen, etc // --------- tableaux de caracteres de taille quelconque ------int main(void){

int nb; // nombre de caracteres

char *cvar1 = "Bonjour!";        // taille calculee a l’initialisation char cvar2[] = "Re-bonjour"; // taille calculee a l’initialisation char *cvar3 = NULL// tableau dynamique de caracteres

printf("Nombre de caracteres (tableaux dynamique et automatique):\n"); scanf("%d", &nb); // choix du nombre de caracteres par l’utilisateur cvar3 = (char *) calloc(nb+1,sizeof(char)); // allocation dynamique char cvar4[nb+1];     // declaration tardive d’un tableau automatique // Affectation des tableaux a l’aide d’une boucle:

                             for(int i=0;i<nb;i++){                    // attention: indice de 0 a nb


cvar3[i] = ’d’; cvar4[i] = ’a’;

}

// Inclusion du caractere de fin de chaine (\0) - imperatif cvar3[nb] = ’\0’; cvar4[nb] = ’\0’;

// Affichage de la taille (sans \0) a l’aide de la fonction strlen:

printf("Longueur 1ere chaine: %d\n", (int) strlen(cvar1)); printf("Longueur 2eme chaine: %d\n", (int) strlen(cvar2)); printf("Longueur 3eme chaine (dynamique): %d\n", (int) strlen(cvar3)); printf("Longueur 4eme chaine (automatique): %d\n", (int) strlen(cvar4)) // Affichage des tableaux de caracteres:

printf("Affichage du premier tableau: %s\n", cvar1); printf("Affichage du deuxieme tableau: %s\n", cvar2); printf("Affichage du tableau dynamique: %s\n", cvar3); printf("Affichage du tableau automatique: %s\n", cvar4); free(cvar3); // liberation de la memoire allouee dynamiquement cvar3=NULL; // par precaution exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

Nombre de caracteres (tableaux dynamique et automatique):

5

Longueur 1ere chaine: 8

Longueur 2eme chaine: 10

Longueur 3eme chaine (dynamique): 5

Longueur 4eme chaine (automatique): 5 Affichage du premier tableau: Bonjour!

Affichage du deuxieme tableau: Re-bonjour

Affichage du tableau dynamique: ddddd

Affichage du tableau automatique: aaaaa

10.4 Declaration et affectation des cha´

                                10.4 Declaration et affectation des chaˆ?nes de caract´               eres`

                                10.4.1       Chaˆ?ne de longueur fixe

–    char st1[4] = "oui" ; (methode conseill´     ee´ ) pas besoin de specifier le caract´     ere de fin de cha`      ˆ?ne \0

–    char st1[4] = {’o’,’u’,’i’,’\0’} ;

                                         il est imperatif d’inclure le caract´  ere de fin de cha` ˆ?ne \0

                                10.4.2        Chaˆ?ne de longueur calculee´     a l’initialisation`

–    char *st2 = "oui" ; (methode conseill´     ee´ )

–    char st2[] = "oui" ; (methode conseill´    ee´ )

NB : ceci n’est possible qu’a la d`       eclaration, car les cha´     ˆ?nes etant des tableaux, on´ ne peut pas faire d’affectation globale (c’est-a-dire de tous les` el´ ements en une´ seule instruction).

L’utilisation de tableaux automatiques et dynamiques est possible.

L’utilisation de boucles pour l’affectation peut s’averer peu pratique.´


10.5         Manipulation des chaˆ?nes de caracteres`

Prototypes des fonctions dans string.h

? directive preprocesseur´              #include <string.h>

10.5.1            Longueur d’une chaˆ?ne avec strlen

Prototype : size_t strlen(const char *s) ;

Calcul de la longueur d’une chaˆ?ne sans le caractere fin de cha`        ˆ?ne.

Exemple :

char *ch="oui"; char ch2[7]="non";

printf("%d\n", sizeof(ch)); // => 4 (’o’ ’u’ ’i’ ’\0’) printf("%d\n", strlen(ch)); // => 3 (’o’ ’u’ ’i’) printf("%d\n", sizeof(ch2)); // => 7 (’n’ ’o’ ’n’ + ’\0’+3) printf("%d\n", strlen(ch2)); // => 3 (’n’ ’o’ ’n’)

   

10.5.2         Concatenation de chaˆ?nes avec´        strcat

Prototype :

char *strcat(char *dest, const char *source) ;

Concatene (ajoute) la cha`         ˆ?ne source a la cha`        ˆ?ne dest et renvoie un pointeur sur dest. Gere le caract`      ere` \0.



Attention : dest doit etre de longueur suffisanteˆ  au risque de segmentation fault. La contrainte sur la taille de dest est :

sizeof(dest) >= strlen(dest) + strlen(source) + 1

Exemple :

strcat(ch2,ch); // concatenation printf("%s\n",ch2); // => nonoui printf("%d\n", strlen(ch2)); // => 6

Noter le format %s dans printf

   

10.5.3           Copie d’une chaˆ?ne avec strcpy

Prototype :

char *strcpy(char *dest, const char *source) ;

Copie la chaˆ?ne source dans la chaˆ?ne dest et renvoie un pointeur sur dest.

La valeur de retour est d’inter´ et limitˆ e.´

Attention : dest doit etre de longueur suffisanteˆ  au risque de segmentation fault. La contrainte sur la taille de dest est :

sizeof(dest) >= strlen(dest) + strlen(source) + 1

Attention : la fonction strcpy ne gere pas le caract`         ere` \0. Le programmeur doit s’assurer qu’il est present dans´ source avant d’effectuer la copie.

Exemple :

char *ch="oui"; // chaine de 3 caracteres + \0 char ch2[7]; // chaine de taille totale 7 strcpy(ch2,ch); // copie printf("%s\n", ch2); // => oui

10.5.4            Comparaison de chaˆ?nes avec strcmp

Prototype :

int strcmp(const char *s1, const char *s2) ;

Renvoie un entier positif, nul ou negatif´ si s1 est plus grand, egal, plus petit que´ s2 (dans l’ordre lexicographique).

Exemple :

char *ch="oui"; char ch2[7]="non";

printf("%d\n", strcmp(ch,ch2));                    // => 1 ("oui" > "non")

10.5.5           Recherche d’un caractere dans une chaˆ?ne avec`          strchr

Prototype : char *strchr(const char *s, int c) ;

Renvoie un pointeur de caractere` vers la premiere occurrence de` c dans la chaˆ?ne s, ou NULL si c n’est pas trouve.´

Exemple :

char *ch="oui"; char *pc; pc = strchr(ch, ’u’);

printf("%d\n", pc - ch);                                     // => 1 (car ch[1]=’u’)

10.5.6                Recherche d’une sous-chaˆ?ne dans une chaˆ?ne avec strstr

Prototype :

char *strstr(const char *foin, const char *aiguille) ;

Renvoie un pointeur vers la premiere occurrence de la sous-cha` ˆ?ne aiguille dans la chaˆ?ne foin, ou NULL si aiguille n’est pas trouvee.´

Exemple :

char *ch2="nonoui"; char *aig="nou", *pc; pc = strstr(ch2, aig); printf("%d\n", pc - ch2);     // => 2


                           11      Entrees–sorties´

                           11.1      Introduction

Jusqu’a pr`   esent, nos programmes fonctionnaient en interactif, et utilisaient les´ entrees-sorties dites´     standard :

–    lecture des entrees sur le´         clavier (stdin)

–    ecriture des sorties sur l’´ ecran´       (stdout)

? utilisation de printf et scanf (prototypes dans stdio.h)

Pour utiliser des informations stockees de fac¸on permanente, ou pour stocker des´ informations, on a besoin de manipuler des fichiers. En particulier : – ouvrir/fermer un fichier a partir d’un programme`     ,

–    acceder au contenu du fichier´         (de maniere s`  equentielle ou directe),´

–    effectuer des operations sur le fichier´   (lecture/ecriture).´

? utilisation de fopen, fclose, fprintf, fscanf,

(prototypes dans stdio.h)

                           11.1.1              Rappel : les fonctions printf et scanf

Les fonctions printf et scanf sont des fonctions d’entree-sortie standard.´

Leur prototype est dans le fichier : stdio.h

                              ? directive preprocesseur (compilation) :´     #include <stdio.h>

Prototypes : int printf(const char* format, ) ; int scanf(const char* format, ) ;

–    valeur de retour du type int

–    printf : le nombre de caracteres` ecrits si tout se passe bien´ ou un nombre negatif en cas d’erreur.´

–    scanf : le nombre de variables effectivement lues si tout se passe bien ou un nombre negatif en cas d’erreur.´

–    nombre variable d’arguments : , dont au moins une chaˆ?ne de caracteres`

Remarque : si on ne s’interesse pas aux valeurs de retour de ces fonctions, elles´ peuvent etre appelˆ    ees comme des fonctions sans retour.´

                           11.1.2       Exemple introductif

/* programme lecture-elem.c */

#include <stdio.h> #include <stdlib.h>

//--------- exemple entree/sortie ------------------------// on suppose qu’on dispose de fichiers contenant deux champs:

// premier champ: nom des etudiants // deuxieme champ: notes des etudiants int main(void) {

int n;

char fichier[80]; // nom externe du fichier

FILE *pfich=NULL; // declaration du flux (nom interne du fichier) char nom[20];   // nom etudiant float note; // note etudiant

printf("Quel fichier ouvrir ?\n"); // affichage a l’ecran scanf("%s", fichier); // lecture du nom de fichier (clavier) pfich=fopen(fichier,"r"); // ouverture du fichier en lecture

                           if (pfich == NULL) {                         // en cas de probleme

exit(EXIT_FAILURE); // arreter l’execution

}

while(1) { // boucle (a priori) infinie // on utilise la valeur de retour de fscanf:

n=fscanf(pfich,"%s %f", nom, &note); // lecture du fichier

if (n == EOF) { // EOF = End Of File break; // on quitte la boucle en fin de fichier

}

                                       printf("%s %g\n", nom, note);           // affichage a l’ecran

}

fclose(pfich); // fermeture du flux exit(EXIT_SUCCESS) ;

}


11.2 Type de fichiers et acces`

11.2        Type de fichiers et acces`

On distingue deux types de fichiers :

–    fichier binaire : fichier dans lequel les donnees sont´    ecrites comme en m´        emoire.´ – fichier formate :´      c’est un fichier texte.

fichiers

 

formates´

binaires

   

saisie et affichage

comme en memoire´

compacite´

 

?

+

rapidite des E/S´

 

?

+

precision conserv´                                 ee´

 

?

+

portabilite´

 

+

?

11.2 Type de fichiers et acces`

                           On distingue deux manieres d’acc` eder aux informations du fichier :´

–    acces s` equentiel (le plus courant) :´    on lit/ecrit dans l’ordre dans lequel les´ informations apparaissent/apparaˆ?teront.

–    acces direct :`        on lit/ecrit´        a un endroit particulier du fichier dont les` enregistrements sont indexes.´

acces aux donn`    ees´

 

sequentiel´

direct

   

lire/ecrire dans l’ordre´

enregistrements indexes´

Par defaut l’acc´    es est s` equentiel.´

L’acces direct se fait au moyen de fonctions`             fseek, ftell,

11.3         Ouverture et fermeture d’un fichier

On associe un nom externe de fichier (une chaˆ?ne de caracteres)` a un`     nom interne dans le programme (un pointeur sur FILE).

Le nom interne est aussi appele´ flux.

11.3.1        Declaration d’un flux´

Un flux est un pointeur sur une structure de type FILE.

FILE est le type d’un objet contenant toutes les informations necessaire´        a la` gestion d’un fichier

La declaration d’un flux´           pfich se fait au moyen de l’instruction :

FILE *pfich = NULL; // pfich: nom interne ou flux

Important : initialiser pfich a` NULL.

Remarque : aucun lien effectif a ce niveau entre le flux et le fichier.`


                           11.3.2          Ouverture d’un flux avec fopen

Le lien entre nom interne et nom externe est fait a l’ouverture d’un flux.`

Prototype :

FILE *fopen(const char *nom_externe, const char *mode) ;

Valeur de retour :

pointeur sur FILE (flux) si tout se passe bien et le pointeur NULL sinon. ? toujours tester la valeur retournee pour s’assurer de la bonne ouverture du flux´

Arguments :

–    nom_externe : chaˆ?ne de caracteres indiquant le nom externe du fichier,`

–    mode : chaˆ?ne de caracteres indiquant le mode d’ouverture du fichier`

valeur

 

position

ajouter (facultatif)

"r"

read

debut du fichier´

+ si mise a jour (lecture+`        ecriture)´

"w"

write

debut du fichier´

b si binaire

"a"

append

fin du fichier

 

Mode "r" : erreur si le fichier n’existe pas

                              Mode "w" : le fichier est creé s’il n’existe pas,´    ecras´      e´ s’il existe

                               Mode "a" : le fichier est creé s’il n’existe pas ;´    ecriture´   a la fin du fichier s’il existe`

                           11.3.3           Fermeture d’un flux avec fclose

Prototype : int fclose(FILE *stream) ;

                           11.3.4          Exemple d’ouverture/fermeture d’un fichier

/* programme fich.c */

#include <stdio.h> #include <stdlib.h> int main(void) {

FILE *pfich = NULL; // important: initialiser pfich a NULL pfich=fopen("","w"); if (pfich == NULL) { // en cas de probeme

printf("Le fichier n’existe pas\n"); exit(EXIT_FAILURE); // arreter l’execution

}

// instructions fclose(pfich); // fermer le flux avant d’arreter l’execution exit(EXIT_SUCCESS);

}

                           11.4       Entree-sorties format´  ees´

Les operations sur les fichiers se font sous forme texte.´

                  Il est recommande d’utiliser un flux ouvert en mode texte (le cas par d´                                                                               efaut).´

                           11.4.1           Ecriture avec fprintf

int fprintf(FILE* stream, const char* format, ) ;

                            Appartient a la famille de`        printf : indiquer le flux en plus.

Valeur de retour : la fonction fprintf renvoie – le nombre de caracteres`  ecrits´        si tout se passe bien, – ou un nombre negatif´   en cas d’erreur.

Remarques :

–    printf = fprintf(stdout, )

–    Pour ecrire des messages d’erreurs, il vaut mieux utiliser :´

fprintf(stderr, "Erreur ") que printf("Erreur ")


11.4.2           Lecture avec fscanf

int fscanf(FILE* stream, const char* format, ) ;

Appartient a la famille de`        scanf : indiquer le flux en plus.

Valeur de retour : la fonction fscanf renvoie – le nombre de variables attendues si tout se passe bien, – un nombre negatif´        en cas d’erreur.

L’entier retourne peut´ etreˆ egal´ a la constante` EOF (end of file) si l’on a atteint la fin du fichier.

Remarque : scanf = fscanf(stdin, )

11.4.3          Bilan sur les entrees-sorties format´    ees´

stdout

printf(format [,liste de variables])

fichier

fprintf(FILE* stream, format [,liste d’expressions])

chaˆ?ne

sprintf(char* string, format [,liste d’expressions])

Il existe deux familles de 3 fonctions en C pour lire/ecrire :´

–    sur les entree/sortie standards´

–    dans les fichiers

–    dans les chaˆ?nes de caracteres`

stdin

scanf(format,liste d’adresses)

fichier

fscanf(FILE* stream, format,liste de pointeurs)

chaˆ?ne

sscanf(char* string, format,liste de pointeurs)

                                                                                                                                                                                                                11.5 Entrees-sorties non format´                                                                                                                           ees (binaires)´

11.5        Entrees-sorties non format´    ees (binaires)´

Les operations sur les fichiers se font sous forme binaire.´

Il est fortement recommande d’utiliser un flux ouvert en mode binaire.´

11.5.1          Lecture avec fread

Prototype :        size_t fread(void *ptr, size_t taille, size_t nbloc, FILE *stream) ;

La fonction fread :

–    lit nbloc (le plus souvent 1) de taille taille dans le flux stream et les ecrit´        a partir de l’adresse` ptr.

–    retourne le nombre de blocs effectivement lu

            ? erreur si ce nombre est different de´       nbloc.

Cette erreur peut etre la rencontre de la fin du fichier.ˆ


                                                                                                                                                                                                                11.5 Entrees-sorties non format´                                                                                                                           ees (binaires)´

11.5.2       Ecriture avec´      fwrite

Prototype :

size_t fwrite(const void *ptr, size_t taille, size_t nbloc, FILE *stream) ;

La fonction fwrite :

–    ecrit´     nbloc (le plus souvent 1) de taille taille situes´       a l’adresse`      ptr dans le flux stream.

–    retourne le nombre de blocs effectivement ecrits´

            ? erreur si ce nombre est different de´       nbloc.

11.6 Retour sur les formats d’entree–sortie´

11.6         Retour sur les formats d’entree–sortie´

(gabarit : w =largeur, p= precision)´

entiers

decimal´

%w[.p]d

octal

%wo

hexadecimal´

%wx

reels´

virgule fixe

%w[.p]f

virgule flottante (notation scientifique)

%w[.p]e

gen´  eral´

(combinaison)

%w[.p]g

 

caracteres`

 
     

caracteres`

 

%w[.p]c

chaˆ?ne

 

%w[.p]s

Voir la section entrees-sorties standard´     el´ ementaires.´

11.6 Retour sur les formats d’entree–sortie´

La largeur w est facultative pour les formats %d, %f, %e, %g, %s

Si la largeur w est insuffisante, elle est elargie pour´ ecrire l’expression.´ Si le nombre de descripteurs 6= nb d’el´ ements de la liste d’E/S, le nb de´ descripteurs prime

? risque d’acces m`   emoire non r´ eserv´ ee´


                           11.7          Exemple de lecture de fichier formate en C´

/* programme lecture.c */

#include <stdio.h> #include <stdlib.h>

/* lecture du fichier donnees */ int main(void) {

char nom[20] ;       /* limitation des chaˆ?nes a` 20 caracteres` */ char article[50] ; int nombre ; float prix, dette ; int n , ligne=1;

FILE *pf = NULL ; /* pointeur sur le flux d’entree´ */ //char *fichier ="donnees"; /* nom du fichier formate´ */ char fichier[80];

printf("Quel fichier ouvrir ?\n");

scanf("%s", fichier); // lecture du nom de fichier pf=fopen(fichier,"r"); // ouverture du fichier if (pf == NULL) { // pb d’ouverture

fprintf(stderr, "erreur ouverture du fichier %s\n", fichier) ; exit (EXIT_FAILURE) ;

}

printf("Le fichier %s s’ouvre correctement\n", fichier) ;

while(1) { // boucle de lecture des donnees´

n=fscanf(pf,"%s %s %d %f",nom,article,&nombre,&prix); if (n == EOF) { // on quitte la boucle en fin de fichier break;

}

if (n == 4) { /* si fscanf a reussi´      a` convertir 4 variables */ dette = nombre * prix ;

printf("%s %s \t %2d x %6.2f = %8.2f\n",nom,article,

nombre,prix,dette); ligne++;

} else {

fprintf(stderr, "problème fscanf ligne %d\n", ligne);

exit (EXIT_FAILURE) ;

}

}

/* sortie normale par EOF */ printf("fin de fichier %d lignes lues \n", ligne-1);

fclose(pf) ;                                    /* fermeture du fichier */

exit(EXIT_SUCCESS) ;

}

donnees

dupond cafe 5 10.5 durand livre 13 60.2 jean disque 5 100.5 paul cafe 6 12.5 jean disque 4 110.75

julie livre 9 110.5

resultat

Quel fichier ouvrir ?

donnees Le fichier donnees s’ouvre correctement

dupond cafe    5 x 10.50 =  52.50 durand livre 13 x 60.20 =        782.60 jean disque        5 x 100.50 = 502.50

paul cafe                                  6 x 12.50 =                 75.00

jean disque      4 x 110.75 =  443.00 julie livre    9 x 110.50 =        994.50

fin de fichier 6 lignes lues


11.8 Fonctions supplementaires´

11.8        Fonctions supplementaires´

Lecture

char *fgets(char *s, int size, FILE *stream)

permet de lire size caracteres dans la cha`        ˆ?ne s a partir du flux` stream (eventuellement´         stdin).

Contrairement a` fscanf(stream, "%s", ), fgets ne s’arrete pasˆ a` la rencontre d’un caractere espace mais en fin de ligne.`

La valeur de retour est l’adresse de la chaˆ?ne lue quand tout s’est bien passe´, ou egale´        a` NULL en cas d’erreur.

11.8 Fonctions supplementaires´

Ecriture´

int fputs(const char *s,

FILE *stream)

permet d’ecrire la cha´      ˆ?ne s dans le flux stream (eventuellement´ stdout ou stderr).

La valeur de retour est non negative´         quand tout s’est bien passe´, ou egale´        a` EOF en cas d’erreur.

12        Structures ou types deriv´   es´

12.1      Inter´ et des structuresˆ

Tableau = agregat d’objets de´  meme typeˆ      reper´ es par un´   indice entier Structure = agregat d’objets de´   types differents´         (chaˆ?nes de caracteres, entiers,` flottants, tableaux ) reper´ es par un´     nom de champ.

? representation de donn´       ees composites et manipulation champ par champ ou´ globale (passage en argument des procedures simplifi´ e)´

Structures statiques : champs de taille fixe

Structures dynamiques : comportant des champs de taille variable

On utilise une structure quand les objets manipules ont´     un lien entre eux :

–    differentes informations associ´    ees´  a une personne : nom, pr`     enom, adresse,´ numero de t´   el´ ephone, ´

–    differentes informations associ´    ees´  a un point du plan : nom, abscisse, ordonn` ee.´

–    echantillon de mesure sous ballon sonde : altitude, pression, temp´   erature,´ humidite, vitesse, orientation du vent.´


                                12.1.1         Exemple introductif de structures

#include <stdio.h> #include <stdlib.h>

#define N 4 // nombre de points

// definition de la structure:

struct point{ // revient a definir un nouveau type

int no;        // numero du point double x; // abscisse du point double y;        // ordonnee du point

}; // ne pas oublier ;

int main(void){

// declarations de variables de type struct point:

struct point p; // variable ordinaire de type struct point struct point courbe[N]; // tableau d’elements de type struct point // affectation de la structure:

for(int i=0; i<N; i++){

p.no = i + 1;  // affectation du premier     champ de p p.x = 1./(i+1.);      // affectation du deuxieme champ de p

p.y = 2.*i;      // affectation du troisieme champ de p courbe[i] = p;   // affectation de l’element i de courbe

}

// affichage des elements de la structure:

printf("Affichage des donnees:\n"); for(int i=0; i<N; i++){ printf("%d %g %g\n", courbe[i].no, courbe[i].x, courbe[i].y); } exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

1 1

0

2 0.5

2

3    0.333333 4

4    0.25   6

Caracteristiques de ce programme :´

–    definition d’une structure´    point,

–    declaration d’une variable du type´        struct point et d’un tableau dont chaque el´ ement est du type´  struct point,

–    affectation des structures champ par champ,

–    affichage des resultats champ par champ.´


12.2      Definition, d´  eclaration et initialisation des structures´

12.2.1         Definition d’un type structure´      point

Definir une structure correspond´    a d` efinir´  un nouveau type :

struct point { // definition de la structure

int no ; // 1er champ double x ; // 2eme champ double y ; // 3eme champ

};

Important :

–    Noter le ; obligatoire apres l’accolade` }.

–    La definition d’une structure ne r´ eserve aucun emplacement m´ emoire associ´  e´ a` cette structure.

12.2.2              Quels types de champs peut-on mettre dans une structure?

–    Tous les types de base du langage (char, int, double )

–    Des structures (sauf du type en train d’etre dˆ      efini)´

–    Des pointeurs (y compris sur une structure du type en train d’etre dˆ     efini)´

–    Des tableaux de taille fixe

–    Des tableaux alloues dynamiquement´         (sur le tas), via un pointeur

            (? Tableaux automatiques deconseill´  es :´               avertissement a la compilation)`

12.2.3          Ou` placer la definition d’une structure?´

La definition de la structure doit´       etre visible dans toutes les fonctions utilisant le typeˆ structure declar´     e. On doit donc d´     efinir la structure en dehors de toute fonction´ (gen´    eralement en´   debut d’un fichier´   ), ou mieux dans un fichier include

(d’extension .h)

12.2.4          Declaration de variables de type structure´        point

Declarer de 2 variables de type´            struct point :

struct point debut, fin ;

Important : c’est la declaration de variables du type´         struct point, ici debut et fin, qui reserve un emplacement m´         emoire de taille suffisante pour stocker tous´ les el´ ements associ´   es´    a chaque variable.`

12.2.5           Affectation d’une structure (constructeur)

Similaire au cas des tableaux :

–    L’affectation globale peut se faire a la d`        eclaration :´ struct point fin={9,5.,2.};

–    En dehors de la declaration, l’affectation se fait champ par champ.´


                                12.3        Manipulation des structures
                                12.3.1          Acces aux champs d’une structure`

Pour acceder aux champs d’une structure, on utilise l’op´ erateur´    . et le nom du champ :

= 1; // affectation du 1er champ debut.x = 0.; // affectation du 2eme champ debut.y = 0.; // affectation du 3eme champ

? L’acces aux diff`    erents champs par leur nom est plus lourd que l’acc´ es aux` el´ ements d’un tableau (pas d’indice´    ? pas de boucle). Ce nommage permet toutefois de rendre les programmes plus lisibles.

                               L’operateur´              . est prioritaire par rapport a` & (adresse) et * (indirection).

                                12.3.2        Affectation globale (meme type)ˆ

Quand 2 structures sont du meme type,ˆ  et uniquement dans ce cas, on peut affecter globalement l’une a l’autre (copie)` fin = debut ;

Avantage sur les tableaux! On dit que les structures sont des lvalue.

Par contre, on ne peut pas tester l’egalit´ e (´ ==) ou la difference (´      !=) entre 2 structures

if (fin == debut) { est illegal!´

                         A fortiori, on ne peut pas utiliser les autres operateurs relationnels (´                                                                                <, > )

                                12.3.3        Entrees/sorties et structures´

Obligatoirement champ par champ :

scanf("%d %g %g", &debut.no, &debut.x, &debut.y); printf("%d %g %g", debut.no, debut.x, debut.y);

                                12.3.4         Retour sur l’exemple introductif

#include <stdio.h> #include <stdlib.h>

#define N 4 // nombre de points

// definition de la structure:

struct point{ // revient a definir un nouveau type

int no;        // numero du point double x; // abscisse du point double y;        // ordonnee du point

}; // ne pas oublier ;

int main(void){

// declarations de variables de type struct point:

struct point p; // variable ordinaire de type struct point struct point courbe[N]; // tableau d’elements de type struct point // affectation de la structure: printf("Entrer les coordonnees des points:\n"); for(int i=0; i<N; i++){

p.no = i + 1; // affectation du premier champ de p

scanf("%lg %lg", &p.x, &p.y); // lecture des coordonnees courbe[i] = p; // affectation de l’element i de courbe }

// affichage des elements de la structure:

printf("Affichage des donnees:\n"); for(int i=0; i<N; i++){ printf("%d %g %g\n", courbe[i].no, courbe[i].x, courbe[i].y); } exit(EXIT_SUCCESS);

}

                                Resultat´  a l’ex` ecution :´

Entrer les coordonnees des points:

0.     1.

1.     1.

2.     1.

1.2 1.1

Affichage des donnees:

1 0 1 2 1 1 3 2 1

4 1.2 1.1


                                                                                                                                                                                  12.4 Representation en m´                                                                                                                                             emoire des structures´

12.4       Representation en m´    emoire des structures´

Comme pour les tableaux : les champs des structures sont stockes dans l’ordre´ de leur declaration et´      a proximit` e les uns des autres en m´ emoire.´ Contrairement aux tableaux : il peut exister des  octets de

remplissage (padding en anglais) entre les differents champs, afin de repecter´ des contraintes d’alignement.

? la taille (au sens de sizeof) d’une structure >= la somme des tailles de ses differents champs.´

struct mini_point{// definition (aucune reservation memoire) float x; // 4 octets short no; // 2 octets

};

printf("%d", sizeof(struct mini_point));

Resultat 8´ ? 2 octets de remplissage (sur une machine 32 bits) entre no et x

12.5 Pointeur sur une structure

12.5         Pointeur sur une structure

On peut declarer un pointeur sur une structure :´

struct mini_point mpo, *pmpo=NULL; // declaration pmpo = &mpo; pmpo pointe alors sur le premier champ de la structure :

pmpo vaut &mpo.no

12.6             Exemples de structures (plus ou moins) complexes

12.6.1         Tableaux de structures

struct point courbe[9] ; // declaration courbe[0].x = 2. ;

abscisse du premier point de la courbe

12.6.2           Structures contenant un tableau (taille fixe)

struct courbe2{ // definition (aucune reservation memoire) double x[10]; double y[10];

};

struct courbe2 c; // declaration

c.x[0]=0.5;

c.y[0]=sqrt(c.x[0]);


12.6.3           Structures contenant un tableau (taille variable)

Les tableaux automatiques (sur la pile) dans les structures provoquent des avertissements a la compilation`      ? leur usage est deconseill´  e.´

Utiliser des tableaux dynamiques (sur le tas) ? pointeurs et malloc ou calloc :

struct courbe2{ // definition (aucune memoire reservee) int n; double *x; double *y;

};

struct courbe2 c; // declaration scanf("d, &c.n); // nombre de points

c.x = (double *)calloc(c.n, sizeof(double)); // allocation

c.y = (double *)calloc(c.n, sizeof(double)); // sur le tas

c.x[0]=0.5;

c.y[0]=sqrt(c.x[0]);

Attention, dans ce cas, aux copies superficielles : (copie du pointeur seulement)

struct courbe2 c1, c2; // declaration

c2 = c1; // copie superficielle free(c1.x); // c2.x ne pointe plus vers une zone valide

Solution : la copie profonde (allouer un espace memoire pour´ c2.x et y copier tous les el´ ements du tableau´         c1.x)

c2 = c1; // affectation des variables ordinaires c2.x = (double *)calloc(c2.n, sizeof(double)); // allocation for (int i=0; i < c2.n; i++) { c2.x[i] = c1.x[i]; // copie profonde

}

free(c1.x); // c2.x continue de pointer vers une zone valide

12.6.4          Structures contenant une structure

struct lieu{ // definition char nom[80]; double longitude; double latitude;

};

struct observation{ // definition struct lieu ville; // imbrication double temperature;

};

struct observation obs; // declaration obs.ville.longitude = 0.; // association de "." obs.ville.latitude = 45.; obs.temperature = 273.15; sprintf(obs.ville.nom, "%s", "Bordeaux");

12.6.5       Listes chaˆ?nees´

Ce sont des structures contenant un pointeur vers une structure du meme typeˆ

struct point { // definition int no ; //1er champ float x ; //2e champ float y ; //3e champ

struct point *next ;

};

Elles permettent de mettre en œuvre des  structures  mathematiques de type´ graphe, arbre, etc.


12.7         Structure et fonction, operateur fl´   eche`

Les structures se comportent comme n’importe quel type du langage.

12.7.1          Passage par copie de valeur

C’est le mode par defaut :´        a utiliser`  quand on ne veut pas modifier l’argument dans la fonction :

void affiche_point(struct point p) {

printf("%d %g %g\n", p.no, p.x, p.y); //affiche seulement

}

struct point po; // declaration affiche(po); // passage par copie de valeur

12.7.2          Passage par copie d’adresse

A utiliser`        quand on veut modifier l’argument dans la fonction :

void init_point(struct point * p) {

(*p).no = 1; // modification des valeurs

(*p).x = 0.;

(*p).y = 0.;

}

struct point po;

affiche(&po); // passage par copie d’adresse

12.7.3      Operateur fl´ eche`

Afin d’eviter la notation lourde :´ (*p_struct).champ utiliser l’operateur´ -> (defini uniquement pour les structures´ ) :

p_struct -> champ

On ecrira la fonction pr´  ec´ edente sous la forme :´

void init_point(struct point * p) { p->no=1; p->x=0.; p->y=0.;

}


12.8 Valeur de retour

12.8       Valeur de retour

Une fonction peut renvoyer une structure (elle est alors du type struct nom struct) :

struct point cree_point(int a, double b, double c){ struct point p; // declaration

p.no = a;

p.x = b;

p.y = c; return p;

} struct point po; int n; double x0, y0;

po = cree_point(n,x0,y0);

12.9       Exemple final

/* Fichier point.h */

#ifndef POINT #define POINT struct point { // definition´    du type point int no; /* numero´ */ float x; /* abscisse */ float y; /* ordonnee */

};

#endif POINT /*POINT */

// programme sym3_pt.c

#include <stdio.h> #include <stdlib.h>

#include "point.h"

struct point psym(struct point m){

// fonction a` valeur de retour de type struct point struct point symetrique; = ; // changement de signe symetrique.x = m.y; // echange´    entre x et y symetrique.y = m.x; return symetrique;

}

void sym(struct point m, struct point *n) {

// chgt de signe de no et echange´    x/y n->no = ; n->x = m.y; n->y = m.x;

}

int main(void){ struct point a ={5, 1. ,-2.}; // declaration´ de a struct point b, c; // declaration´     de type struct point

// affichage des champs de a printf("a = %d %g %g\n", a.no, a.x, a.y);

// calcul et affichage de b

b=psym(a); printf("psym(a) = %d %g %g\n", b.no, b.x, b.y);

// calcul et affichage de c sym(a, &c); printf("sym. de a = %d %g %g\n", c.no, c.x, c.y); exit(EXIT_SUCCESS);

}


12.10 Bilan sur les structures Resultat´ a l’ex` ecution :´

a = 5 1 -2

psym(a) = -5 -2 1 sym. de a = -5 -2 1

12.10         Bilan sur les structures

Les structures permettent de regrouper des donnees h´   et´ erog´   enes pour les` communiquer de fac¸on plus concise entre fonctions.

La notion de structure devient beaucoup plus puissante pour manipuler des objets complexes si on lui associe des methodes de manipulation´    sous forme de

–    fonctions (possible en C)

–    operateurs (impossible en C mais possible en C++)´

? programmation objet

13       El´ ements de compilation s´  epar´ ee´

13.1       Introduction

Il est d’usage de separer les programmes´            longs  en plusieurs fichiers :

–    il est plus facile et rapide de compiler separ´ ement des´        entites de programme´ courtes que des centaines de lignes de code,

–    la separation en plusieurs fichiers permet de´        structurer un programme

          ? fichier = unite coh´   erente contenant une ou quelques fonctions,´

–    le decoupage en unit´   es coh´      erentes permet une´ re-utilisation plus facile du´ code,

–    les differents fichiers peuvent´      etre rassemblˆ  es dans des´     bibliotheques`

(collections de fichiers objets),

–    il est possible d’automatiser la compilation separ´         ee´    a l’aide de l’utilitaire`         make.

13.1 Introduction

Le decoupage en plusieurs fichiers induit des´         contraintes.

Il faut :

–    permettre au compilateur de verifier la coh´ erence entre d´     efinition/appels des´ fonctions : assurer la visibilite des prototypes´  ,

–    acceder aux nouveaux types d´    efinis´       (struct ) dans tous les fichiers ou ils sont d`     efinis,´

Dans les 2 cas, une mise en œuvre robuste de la compilation separ´ ee r´ eside dans´ l’utilisation de fichiers d’enteteˆ    (header files en anglais).


13.2       Fichiers d’entete (ˆ   header files)

13.2.1       Definition et usage´

Ces fichiers peuvent contenir des declarations de fonctions (´   prototypes) ou de nouveaux types.

Pour les fonctions, ils doivent etre inclus dans :ˆ

–    le fichier ou` la fonction est definie´     , pour assurer la coherence´ declaration/d´       efinition,´

–    les fichiers ou` la fonction est appelee´ , pour assurer la coherence´ appel/declaration.´

L’inclusion se fait au moyen d’une directive preprocesseur :´     #include. Pour les fonctions comme pour les types, il faut se proteger contre les´ inclusions multiples.

 

fichier exécutable

a.out

286

13.2.2        Structure d’un fichier d’enteteˆ

Soit un fichier fct.c contenant la definition de plusieurs fonctions. Le fichier´ fct.h correspondant peut s’ecrire :´

#ifndef FCT                                // si FCT n’est pas defini

#define FCT  // definir FCT double produit(double x, double y); void affiche(double res);

#endif                                    // fin du if

Dans l’exemple ci dessus, ce sont les directives preprocesseur qui prot´ egent contre` les inclusions multiples.


13.3           Exemple de programme en plusieurs fichiers

/* fichier main.c */

#include <stdio.h> // prototypes de printf, scanf

#include <stdlib.h> // prototype de exit

#include "fct.h"                                 // prototypes de produit et affiche

int main(void){ double a, b, prod; printf("Entrer deux nombres reels:\n"); scanf("%lg %lg", &a, &b); prod = produit(a,b); // appel de produit affiche(prod);    // appel de affiche

exit(EXIT_SUCCESS);

}

 

/* fichier fct.h : declaration des fonctions */

#ifndef FCT #define FCT double produit(double x, double y); void affiche(double res);

#endif /* FCT */

/* fichier fct.c : definition des fonctions */

#include <stdio.h>

#include <stdlib.h>

#include "fct.h" // declaration de produit et affiche

double produit(double x, double y){

return x*y;

}

void affiche(double res){

printf("produit = %g\n", res);

}

Compilation :

gcc-mni-c99 -c main.c fct.c

? creation de 2 fichiers objet :´          main.o et fct.o,

Edition de liens :

gcc-mni-c99 main.o fct.o -o main.x

? creation d’un fichier ex´  ecutable´   main.x

Caracteristiques de ce programme :´            3 types de fichiers

–    le fichier principal main.c : appel et declaration de deux fonctions,´

–    le fichier fct.c : declaration et d´  efinition de deux fonctions,´

–    le fichier d’enteteˆ fct.h : declaration de deux fonctions d´  efinies dans´     fct.c et appelees dans´     main.c


13.4          Bibliotheques statiques de fichiers objets`

–    Inter´ et :ˆ     regrouper dans un seul fichier toute une collection de fichiers objets de fonctions compilees pour simplifier les futures commandes d’´     edition de lien qui´ utilisent ces fonctions.

–    Interface : regrouper les prototypes de toutes les fonctions de la bibliotheque` dans un seul fichier (.h).

13.4.1         Creation et utilisation d’une biblioth´     eque statique (archive)`

Pour creer une biblioth´  eque de nom`     libtab il faut :

1.    compiler les fichiers a ins`  erer dans la biblioth´ eque`

gcc -c double1d.c double1d_libere.c

2.    inserer les objets dans la biblioth´       eque (statique) :`

ar rv libtab.a double1d.o double1d_libere.o

3.    creer un fichier´ tab.h contenant le prototype des fonctions utilisees´

? #include "tab.h" dans le code source utilisant la bibliotheque`

4.    puiser dans la bibliotheque lors de l’` edition de lien :´

–    en donnant le nom de l’archive pour un premier test : (les fichiers sont alors tous dans le repertoire courant)´ gcc main.c libtab.a

–    ou en precisant le chemin d’acc´    es :`

gcc -I/home/user/include -L/home/user/lib main.c -ltab ou` tab.h est dans le repertoire´   /home/user/include l’archive libtab.a est dans le repertoire´    /home/user/lib et les deux chemins dependent de l’utilisateur de nom´  user

Remarque importante : les options -I et -L ne s’utilisent que pour une bibliotheque`      non standard.

Commande de gestion des bibliotheques statiques :` ar (cf tar) Actions principales :

r ajout ou remplacement d’une liste de membres

ar rv libtab.a double1d.o double1d_libere.o t liste les membres de la bibliotheque` ar tv libtab.a

rw-r--r-- 904/800

860 Jan 29 11:17 2008 double1d.o

rw-r--r-- 904/800

808 Jan 29 11:17 2008 double1d_libere.o

x extraction d’une liste de membres

ar xv libtab.a double1d.o d destruction d’une liste de membres

ar dv libtab.a double1d_libere.o v option (verbose) avec messages d’information u option (update) mise a jour seulement` ? ar ruv

13.4.2          Retour sur la bibliotheque standard`

La bibliotheque standard est elle-m`    eme composˆ ee de sous-biblioth´                                                                             eques.`

(les fichiers d’archive associes sont dans :´                 /usr/lib ou /lib, )

A chaque sous-bibliotheque est associ`      e un fichier d’ent´ ete :ˆ

(ces fichiers sont dans : /usr/include ou /include, )

–    stdio.h : prototypes de scanf, printf, fopen, fclose,

–    stdlib.h : prototype d’exit, definition de´   EXIT_SUCCESS, EXIT_FAILURE,

–    tgmath.h (en C99) : prototype des fonctions mathematiques.´ –

Dans le code source : le fichier d’entete est entreˆ                 < >

Edition de liens : automatique pour toutes les sous-bibliotheques sauf la` sous-bibliotheque math` ematique :´ il faut utiliser l’option -lm de gcc.

13.4.3        Retour sur la bibliotheque`        libmnitab

Le fichier d’archive associe´ a cette biblioth`    eque est :`      libmnitab.a

Le prototype des fonctions de cette bibliotheque est dans le fichier`                                                                                                                         mnitab.h

Dans le code source : #include "mnitab.h"

A la compilation : sur sappli utiliser gcc+mni ou gcc+mni-c99

? ceci correspond a un alias vers :`

gcc -I/home/lefrere/include -L/home/lefrere/lib ou le r` epertoire´ /home/lefrere/include contient mnitab.h et le repertoire´ /home/lefrere/lib contient libmnitab.a

Edition de liens : ajouter l’option -lmnitab
 

Remarque : en gen´  eral, la cr´ eation d’une biblioth´ eque n’a d’int`  eret que si elle´ contient de nombreuses fonctions que l’on n’a pas besoin de modifier. Sinon, pref´ erer une compilation en fichiers s´    epar´         es sans biblioth´    eque ou l’utilisation de` l’utilitaire make.


13.5       Gen´ eration d’un fichier ex´  ecutable avec´   make

13.5.1       Principe

La commande make permet d’automatiser la gen´ eration d’un fichier ex´ ecutable ou´ cible (target) qui depend´ d’autres fichiers en mettant en œuvre certaines regles` (rules) de construction decrites dans un fichier´ makefile.

make minimise les operations de mise´   a jour en s’appuyant sur les r` egles de` dependance et les´     dates de modification des fichiers.

Application la plus classique :

reconstituer automatiquement un programme executable´ a partir des fichiers` sources en ne recompilant que ceux qui ont et´ e modifi´        es.´

–    cible (target) : en gen´  eral un fichier´  a produire`

–    regle`   de production (rule) : liste des commandes a ex`       ecuter pour construire une´ cible (compilation pour les fichiers objets, edition de lien pour l’ex´      ecutable)´

–    dependance´         : ensemble des fichiers necessaires´  a la production d’une cible`

13.5.2            Construction d’un makefile

Le fichier makefile liste les cibles, decrit les d´             ependances et les r´                                                                                                                 egles.`

Syntaxe des dependances :´

cible: liste des dépendances

(tabulation) règle de construction

? necessite d’int´    egrer des commandes shell dans le´     makefile Le fichier makefile est construit a partir de l’arbre des d` ependances.´

gcc -MM fichier.c affiche les dependances de´                            fichier.o

(necessite les´    .h)

13.5.3       Exemple el´ ementaire de´      makefile en C
 

## fichier makefile construit a partir de l’arbre des dependa

# premiere` cible = executable´                           => regle`               = edition´                                                                       de liens

a.out : fct.o main.o

TABgcc fct.o main.o

# cibles des objets => regle`     = compilation seule avec gcc -c fct.o : fct.c fct.h

TABgcc -c fct.c main.o : main.c fct.h TABgcc -c main.c

# menage´     : suppression des fichiers reconstructibles clean:

TAB/bin/rm -f a.out *.o

13.5.4           Utilisation d’un makefile

make cible

lance la production de la cible en exploitant le fichier makefile du repertoire courant.´

make -n cible

affiche les commandes que devrait lancer make pour produire la cible

Remarque : cmake est un utilitaire equivalent´        a` make et adapte´ a un ensemble` plus large de systemes d’exploitation (dont Windows).`

Voir :


14       Conclusions

Le langage C est un langage tres complet`           :

–    a la fois haut niveau et bas niveau,`

–    applications : du numerique pour les physiciens´  a l’`   ecriture de syst´     emes` d’exploitation.

Une bonne connaissance du langage C permet d’aborder :

–    des langages tels que le Fortran (FormulaTranslator)

             (passer de Fortran a C peut s’av`   erer plus compliqu´ e que le contraire)´

–    des langages comme PHP et java dont la syntaxe est proche de celle du C,

–    des langages orientes objet comme le´ C++,

–    l’interaction entre langage et systeme d’exploitation (plus difficile en fortran)`

(commande de processus, acquisition de donnees, )´

Annexe A : systemes de num`       eration´

Systeme d`  ecimal´

Representation des nombres en base 10.´

Les chiffres de la numeration d´     ecimale sont les entiers de´  0 a` 9.

Exemple : 75000(10) = 7 × 104 + 5 × 103 + 0 × 102 + 0 × 101 + 0 × 100

Systeme binaire`

Representation des nombres en base 2.´

Les chiffres de la numeration binaire sont le´       0 et le 1.

Chaque chiffre correspond a un`      bit.

Exemples :

–    10(2) = 1 × 21 + 0 × 20 = 2(10)

–    1010(2) = 1 × 23 + 0 × 22 + 1 × 21 + 0 × 20 = 10(10)

Passage d’une base b au systeme d`         ecimal´

On note rn?1 r1r0 (b), avec 0 ? ri < b (i = 0,1, ,n ? 1), la representation´ en base b d’un nombre compose de´        n chiffres.

La valeur decimale de ce nombre, not´ ee´ p ou p(10), est alors donnee par :´

.

Application : b = 2, n = 8 (ensemble de 8 bits ou octet) le cas ou` r0 = 0, r1 = 1, r2 = 0, r3 = 1 et ri>3 = 0

donne bien 00001010(2) = 10(10).

Passage du systeme d`      ecimal´  a une base`  b

Decomposer un nombre d´   ecimal, not´ e´ p ou p(10), en base b :

n?1

p(10) = X ri × bi = rn?1 r1r0 (b),

i=0

consiste a trouver le nombre de bits`      n et le poids affecte´ a chaque bit`                                                                                                        ri.

Soient, q0 = p ÷ b et qi+1 = qi ÷ b quotient de la division entiere de`  qi par b.

On a alors :

–    ri = qi % b, le reste de la division entiere de`        qi par b.

–    n ? 1, l’indice du premier quotient nul : qn?1 = 0.

Application : soient p = 6 et b = 2. On a alors :

–    q0 = 3, r0 = 0,

–    q1 = 1, r1 = 1, – q2 = 0, r2 = 1 ? n = 3.

Donc : 6(10) = 110(2).

Tableau de passage entre systeme binaire et syst`           eme d`                                                                                                         ecimal´

decimal´

binaire

 

decimal´

binaire

0

0

8

1000

1

1

9

1001

2

10

10

1010

3

11

11

1011

4

100

12

1100

5

101

13

1101

6

110

14

1110

7

111

15

1111

 



119