-
orestis.malaspin authoredorestis.malaspin authored
- Types complexes: struct{.C} (1/N)
- Généralités
- Types complexes: struct{.C} (2/N)
- Simplifications
- Types complexes: struct{.C} (3/N)
- Pointeurs
- Allocation dynamique de mémoire (1/N)
- Allocation dynamique de mémoire (2/N)
- Allocation dynamique de mémoire (3/N)
- Allocation dynamique de mémoire (4/N)
- Tableaux dynamiques
- Arithmétique de pointeurs
- Allocation dynamique de mémoire (5/N)
- Arithmétique de pointeurs
- Allocation dynamique de mémoire (6/N)
- Pointeur de pointeur
- Allocation dynamique de mémoire (7/N)
- Pointeur de pointeur
- Allocation dynamique de mémoire (8/N)
- Prototypes de fonctions (1/N)
- Principes généraux de programmation
- Mais pourquoi?
- Exemple
- Prototypes de fonctions (2/N)
- Prototypes de fonctions (3/N)
- Fichier header
- Génération d'un exécutable (1/N)
- Un seul fichier source
- Génération d'un exécutable (2/N)
- Un seul fichier source
- Génération d'un exécutable (3/N)
- Plusieurs fichiers sources
- Génération d'un exécutable (4/N)
- main.c
- sum.h
- sum.c
- Génération d'un exécutable (4/N)
- Compilation séparée
- Exemple
- Préprocesseur (1/N)
- Généralités
- La directive define{.C}
- Préprocesseur (2/N)
- La directive include{.C}
% Base III % Inspirés des slides de F. Glück % 2 octobre 2019
struct
{.C} (1/N)
Types complexes: Généralités
-
Plusieurs variables qu'on aimerait regrouper dans un seul type:
struct
{.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}".num.re = 1.0; num.im = -2.0;
struct
{.C} (2/N)
Types complexes: Simplifications
-
typedef
{.C} permet de définir un nouveau type.typedef unsinged int uint; typedef struct complex complex_t; typedef struct complex { double re, im; } complex_t;
-
L'initialisation peut aussi se faire avec
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
struct
{.C} (3/N)
Types complexes: Pointeurs
-
Comme pour tout type, on peut avoir des pointeurs vers un
struct
{.C}. -
Les champs sont accessible avec le sélecteur
->
{.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ë.#include <stdlib.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:
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
fuites mémoires.
Allocation dynamique de mémoire (3/N)
-
La fonction
free()
{.C} permet de libérer une zone préalablement allouée avecmalloc()
{.C}.#include <stdlib.h> void free(void *ptr);
-
Pour chaque
malloc()
{.C} doit correspondre exactement unfree()
{.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:
int *p = malloc(50 * sizeof(int));
-
Cette espace peut alors être utilisé comme un tableau de 50 entiers:
for (int i = 0; i < 50; ++i) { p[i] = 0; }
Arithmétique de pointeurs
-
On peut parcourir la mémoire différemment qu'avec l'indexation
int *p = malloc(50 * sizeof(int)); // initialize somehow double a = p[7]; double b = *(p + 7); // on avance de 7 "double" p[0] == *p; // le pointeur est le premier élément
Allocation dynamique de mémoire (5/N)
Arithmétique de pointeurs
Allocation dynamique de mémoire (6/N)
Pointeur de pointeur
-
Tout comme une valeur a une adresse, un pointeur a lui-même une adresse:
int a = 2; int *b = &a; int **c = &b;
-
Chaque
*
{.C} ou&
{.C} rajoute une indirection.
Allocation dynamique de mémoire (7/N)
Pointeur de pointeur
Allocation dynamique de mémoire (8/N)
-
Avec
malloc()
, on peut allouer dynamiquement des tableaux de pointeurs:int **p = malloc(50 * sizeof(int*)); for (int i = 0; i < 50; ++i) { p[i] = malloc(70 * sizeof(int)); } int a = p[5][8]; // on indexe dans chaque dimension
-
Ceci est une matrice (un tableau de tableau).
Prototypes de fonctions (1/N)
Principes généraux de programmation
- Beaucoup de fonctionnalités dans un code Modularisation.
- Modularisation du code écriture de fonctions.
- Beaucoup de fonctions 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:
- Utilisation de fonctions dans des fichiers séparés.
- 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}:#include <stdio.h> // libraire dans LD_LIBRARY_PATH #include "chemin/du/prototypes.h"// chemin explicite
Génération d'un exécutable (1/N)
Un seul fichier source
Génération d'un exécutable (2/N)
Un seul fichier source
gcc proc.c -o prog
- **Précompilation: **
gcc
appellecpp
, le préprocesseur qui effectue de la substitution de texte (#define
,#include
, macros, ...) et génère le codeC
à compiler, portant l'extension.i
(prog.i
). - **Compilation assembleur: **
gcc
compile le code C en code assembleur, portant l'extension.s
(prog.s
). - **Compilation code objet: **
gcc
appelleas
, l'assembleur, qui compile le code assembleur en code machine (code objet) portant l'extension.o
(prog.o
). - **Édition des liens: **
gcc
appelleld
, 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.
Génération d'un exécutable (3/N)
Plusieurs fichiers sources
Génération d'un exécutable (4/N)
::: Main
main.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
#ifndef _SUM_H_
#define _SUM_H_
int sum(int tab[], int n);
#endif
::: ::: {.column width="55%"}
sum.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
- Générer séparément les fichiers
.o
avec l'option-c
. - Éditer les liens avec l'option
-o
pour générer l'exécutable.
Exemple
-
Création des fichiers objets,
main.o
etsum.o
$ gcc -Wall -Wextra -std=c11 -c main.c $ gcc -Wall -Wextra -std=c11 -c sum.c
-
Édition des liens
$ gcc main.o sum.o -o prog
Préprocesseur (1/N)
Généralités
- Première étape de la chaîne de compilation.
- Géré automatiquement par
gcc
ouclang
. - Lit et interprète certaines directives:
- Les commentaires (
//
{.C} et/* ... */
{.C}). - Les commandes commençant par
#
{.C}.
- Les commentaires (
- Le préprocesseur ne compile rien, mais subtitue uniquement du texte.
define
{.C}
La directive -
Permet de définir un symbole:
#define PI 3.14159 #define _SUM_H_
-
Permet de définir une macro.
#define NOM_MACRO(arg1, arg2, ...) [code]
Préprocesseur (2/N)
include
{.C}
La directive -
Permet d'inclure un fichier.
-
Le contenu du fichier est ajouté à l'endroit du
#include
{.C}. -
Inclusion de fichiers "globaux" ou "locaux"
#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:
#ifndef _VAR_ #define _VAR_ /* commentaires */ #endif