Newer
Older
% Programmation séquentielle en C
% Base III - Inspirés des slides de F. Glück
% 2 octobre 2019
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Types complexes: `struct`{.C} (1/N)
## Généralités
- Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}.
```C
struct complex { // déclaration
double re;
double im;
};
struct complex num; // déclaration de num
```
- Les champs sont accessible avec le sélecteur "`.`{.C}".
```C
num.re = 1.0;
num.im = -2.0;
```
# Types complexes: `struct`{.C} (2/N)
## Simplifications
- `typedef`{.C} permet de définir un nouveau type.
```C
typedef unsinged int uint;
typedef struct complex complex_t;
typedef struct _complex {
double re, im;
} complex_t;
```
- L'initialisation peut aussi se faire avec
```C
complex_t num = {1.0, -2.0}; // re = 1.0, im = -2.0
complex_t num = {.im = 1.0, .re = -2.0};
complex_t num = {.im = 1.0}; // argl! .re non initialisé
complex_t num2 = num; // copie
```
# Types complexes: `struct`{.C} (3/N)
## Pointeurs
- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}.
- Les champs sont accessible avec le sélecteur `->`{.C}
```C
complex_t *num; // on crée un pointeur
num->re = 1.0; // seg fault...
num->im = -1.0; // mémoire pas allouée.
```
# Allocation dynamique de mémoire (1/N)
- La fonction `malloc`{.C} permet d'allouer dynamiquement (pendant l'exécution du programme) une zone de mémoire contiguë.
```C
#include <stdio.h>
void *malloc(size_t size);
```
- `size`{.C} est la taille de la zone mémoire **en octets**.
- Retourne un pointeur sur la zone mémoire ou `NULL`{.C} en cas d'échec: **toujours vérifier** que la valeur retournée est `!= NULL`{.C}.
# Allocation dynamique de mémoire (2/N)
- Avec l'exemple de tout à l'heure:
```C
complex_t *num = malloc(sizeof(complex_t));
num->re = 1.0; // maintenant ...
num->im = -1.0; // ça marche.
```
- La zone mémoire **n'est pas** initialisée.
- La mémoire doit être désallouée explicitement $\Rightarrow$ **fuites mémoires**.
- Toujours garder un pointeur sur la mémoire allouée sinon **pointeur pendouillant**.
# Allocation dynamique de mémoire (3/N)
- La fonction `free()`{.C} permet de libérer une zone préalablement allouée avec `malloc()`{.C}.
```C
#include <stdlib.h>
void free(void *ptr);
```
- Pour chaque `malloc()`{.C} doit correspondre exactement un `free()`{.C}.
- Si la mémoire n'est pas libérée: **fuite mémoire** (l'ordinateur plante quand il y a plus de mémoire).
- Si la mémoire est **libérée deux** fois: seg fault.
- Pour éviter les mauvaises surprises mettre `ptr`{.C} à `NULL`{.C}.
# Allocation dynamique de mémoire (4/N)
## Tableaux dynamiques
- Pour allouer un espace mémoire de 50 entiers:
```C
int *p = malloc(50 * sizeof(int));
```
- Cette espace peut alors être utilisé comme un tableau de 50 entiers:
```C
for (int i = 0; i < 50; ++i) {
p[i] = 0;
}
```
## Arithmétique de pointeurs
- Autre façon d'indéxer un tableau
```C
int *p = malloc(50 * sizeof(int)); // initialize somehow
double a = p[7];
double b = *(p + 7); // on avance de 7 "double"
p[0] == *p; // rappel, le pointeur est le premier élément
```
# Allocation dynamique de mémoire (5/N)
## Pointeur de pointeur
- Tout comme une valeur a une adresse, un pointeur a lui-même une adresse:
```C
int a = 2;
int *b = &a;
int **c = &b;
```
- Chaque `*`{.C} ou `&`{.C} rajoute une indirection.
- Avec `malloc()`, on peut allouer dynamiquement des tableaux de pointeurs:
```C
int **p = malloc(50 * sizeof(int*));
for (int i = 0; i < 50; ++i) {
p[i] = malloc(70 * sizeof(int));
}
```
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# Prototypes de fonctions (1/N)
## Principes généraux de programmation
- Beaucoup de fonctionnalités dans un code $\Rightarrow$ Modularisation.
- Modularisation du code $\Rightarrow$ écriture de fonctions.
- Beaucoup de fonctions $\Rightarrow$ regrouper les fonctions dans des fichiers séparés.
## Mais pourquoi?
- Lisibilité.
- Raisonnement sur le code.
- Débogage.
## Exemple
- Libraire `stdio.h`: `printf()`{.C}, `scanf()`{.C}, ...
# Prototypes de fonctions (2/N)
- Prototypes de fonctions nécessaires quand:
1. Utilisation de fonctions dans des fichiers séparés.
2. Utilisation de librairies.
- Un prototype indique au compilateur la signature d'une fonction.
- On met les prototypes des fonctions **publiques** dans des fichiers *headers*, extension `.h`.
- Les *implémentations* des fonctions vont dans des fichier `.c`.
# Prototypes de fonctions (3/N)
## Fichier header
- Porte l'extension `.h`
- Contient:
- définitions des types
- prototypes de fonctions
- macros
- directives préprocesseur (cf. plus loin)
- Utilisé pour décrire **l'interface** d'une librairie ou d'un module.
- Un fichier `C` (extension `.c`) utilise un header en *l'important* avec la directive `#include`{.C}:
```C
#include <stdio.h> // libraire dans LD_LIBRARY_PATH
#include "chemin/du/prototypes.h"// chemin explicite
```
## Un seul fichier source
{#fig:compilation width=100%}
# Génération d'un exécutable (2/N)
## Un seul fichier source
```bash
gcc proc.c -o prog
```
1. **Précompilation: ** `gcc` appelle `cpp`, le préprocesseur qui effectue de la substitution de texte (`#define`, `#include`, macros, ...) et génère le code `C` à compiler, portant l'extension `.i` (`prog.i`).
2. **Compilation assembleur: ** `gcc` compile le code C en code assembleur, portant l'extension `.s` (`prog.s`).
3. **Compilation code objet: ** `gcc` appelle `as`, l'assembleur, qui compile le code assembleur en code machine (code objet) portant l'extension `.o` (`prog.o`).
4. **Édition des liens: ** `gcc` appelle `ld`, l'éditeur de liens, qui lie le code objet avec les librairies et d'autres codes objet pour produire l'exécutable final (`prog`).
Les différents codes intermédiaires sont effacés.
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
## Plusieurs fichiers sources
{#fig:compilation_plusieurs width=100%}
# Génération d'un exécutable (4/N)
::: Main
## `main.c`
```C
#include <stdio.h>
#include "sum.h"
int main() {
int tab[] = {1, 2, 3, 4};
printf("sum: %d\n", sum(tab, 4));
return 0;
}
```
:::
:::::::::::::: {.columns}
::: {.column width="45%"}
## `sum.h`
```C
#ifndef _SUM_H_
#define _SUM_H_
int sum(int tab[], int n);
#endif
```
:::
::: {.column width="55%"}
## `sum.c`
```C
#include "sum.h"
int sum(int tab[], int n) {
int s = 0;
for (int i = 0; i < n; i++) {
s += tab[i];
}
return s;
}
```
:::
::::::::::::::
# Génération d'un exécutable (4/N)
La compilation séparée se fait en plusieurs étapes.
## Compilation séparée
1. Générer séparément les fichiers `.o` avec l'option `-c`.
2. Éditer les liens avec l'option `-o` pour générer l'exécutable.
## Exemple
- Création des fichiers objets, `main.o` et `sum.o`
```bash
$ gcc -Wall -Wextra -std=c11 -c main.c
$ gcc -Wall -Wextra -std=c11 -c sum.c
```
- Édition des liens
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# Préprocesseur (1/N)
## Généralités
- Première étape de la chaîne de compilation.
- Géré automatiquement par `gcc` ou `clang`.
- Lit et interprète certaines directives:
1. Les commentaires (`//`{.C} et `/* ... */`{.C}).
2. Les commandes commençant par `#`{.C}.
- Le préprocesseur ne compile rien, mais subtitue uniquement du texte.
## La directive `define`{.C}
- Permet de définir un symbole:
```C
#define PI 3.14159
#define _SUM_H_
```
- Permet de définir une macro.
```C
#define NOM_MACRO(arg1, arg2, ...) [code]
```
# Préprocesseur (2/N)
## La directive `include`{.C}
- Permet d'inclure un fichier.
- Le contenu du fichier est ajouté à l'endroit du `#include`{.C}.
- Inclusion de fichiers "globaux" ou "locaux"
```C
#include <file.h> // LD_LIBRARY_PATH
#include "other_file.h" // local path
```
- Les inclusions multiples peuvent poser problème: définitions multiples. Les headers commencent par:
```C
#ifndef _VAR_
#define _VAR_
/*
commentaires
*/
#endif
```