I. Structure▲
I-A. Qu'est-ce qu’une structure ?▲
La structure en programmation est un type de données qui permet de regrouper en une seule unité plusieurs variables éventuellement de types différents. C'est UNE variable qui est un ensemble de variables. C'est très utile pour composer des objets complexes qui réunissent différentes facettes. Par exemple, si l'on veut faire un carnet de rendez-vous, chaque rendez-vous suppose des informations du genre :
- un libellé ;
- un lieu ;
- une date ;
- un horaire de début ;
- un horaire de fin ;
- une catégorie (bureau, perso, loisir, etc.) ;
- etc.
Toutes ces informations peuvent être regroupées au sein d'une structure et plusieurs structures permettront d'avoir plusieurs rendez-vous. L'intérêt ensuite est de pouvoir avoir un nombre illimité de rendez-vous. Autre exemple dans un jeu vidéo en 2D un ennemi est défini par une position, un déplacement, un type (rampant, grouillant, serpentant, plombant, assommant), un taux d'agressivité, une couleur, une ou plusieurs images, etc.
Pour chacune de ces informations, il s'agit premièrement de choisir un type de variable approprié et ensuite de regrouper toutes ces informations au sein d'une structure « ennemi » afin de pouvoir gérer un ou plusieurs ennemis dans le jeu. Chaque élément de la structure est appelé un « champ ». Notre structure rendez-vous a au moins six champs et la structure ennemie au moins huit champs (position et déplacement horizontaux et verticaux, type…).
I-B. Disposer d’une structure dans un programme▲
I-B-1. Définir un type de structure▲
À la base pour définir une structure, on utilise le mot-clé struct suivi d’une liste de déclarations de variables (sans initialisation) entre accolades et d'un point-virgule :
struct
{
float
x,y; //
attention
pas
possible
d'initialiser
float
dx,dy; //
ses
variables
lors
de
la
définition
int
color; //
de
la
structure
char
lettre;
}
;
Tel quel c'est bien un type de structure, mais comme il n'a pas de nom, il n'est pas possible de l'utiliser dans tout le programme. Pour nommer son type de structure, il suffit d'ajouter le nom voulu après le mot-clé struct :
struct
entite {
//
nommer
son
type
de
structure
float
x,y;
float
dx,dy;
int
color;
char
lettre;
}
;
Nous venons de définir le type de structure « struct entite ». Attention c'est un nom de type, cela ne correspond pas à une variable, mais à un type de variable.
I-B-2. Où placer sa définition de structure▲
En général la définition d'un type de structure se place en global. Au début du fichier juste après les includes si le programme tient sur un seul fichier C. S'il y a plusieurs fichiers C cette définition sera placée dans un fichier d'en-tête .h et ce fichier d'en-tête sera inclus dans tous les fichiers C de code qui ont besoin de cette définition pour fonctionner.
Il est possible de définir un type de structure dans une fonction, mais dans ce cas, ce type ne sera connu que dans le bloc de la fonction et invisible partout ailleurs.
I-B-3. Déclarer ses variables structure▲
Après avoir défini un type de structure, pour avoir des variables de ce type, c'est identique aux autres variables. Il faut indiquer le type, donner un nom pour la variable et clore avec un point-virgule.
Pour avoir des variables du type struct entite défini ci-dessus, nous écrirons quelque part dans le programme :
struct
entite e1, e2;
e1 et e2 sont deux variables du type struct entite.
Remarque
Le nom du type de la structure est indépendant de celui de la variable de ce type et il est possible d'avoir une variable du même nom :
struct
entite entite; //
fonctionne
Par ailleurs il est possible de combiner une définition de type de structure et des déclarations de variables de ce type de structure. La définition de la structure pix ci-dessous peut être suivie d’une suite de déclarations de variables :
struct
pix{
int
x, y;
int
color;
}
P1, P2, toto;
La définition de la structure struct pix est suivie des déclarations des variables P1, P2 et toto qui sont trois variables de type struct pix.
I-B-4. Utiliser un typedef▲
Lorsque l'on utilise des structures en environnement C, il est fastidieux de toujours devoir ajouter le mot-clé struct. Le typedef permet de s'en débarrasser.
La commande typedef donne la possibilité de définir ses propres types à partir des types de base. Le principe est simple : déclarer une variable du type voulu, ajouter typedef devant… et le nom de la variable devient synonyme du type de cette variable. Par exemple :
typedef
int
toto;
permet de définir le type « toto » dans un programme ; toto devient synonyme de int et peut être utilisé à la place dans le programme :
#
include
<stdio.h>
#
include
<stdlib.h>
typedef
int
toto; //
création
du
type
toto
int
main
(
)
{
toto test =
80
;
printf
(
"
la
variable
test
vaut
:
%d\n
"
, test);
system
(
"
PAUSE
"
);
return
0
;
}
L'intérêt de pouvoir rebaptiser ses types dépend de certaines situations, par exemple créer un langage en français. Mais dans un environnement C typedef est utilisé systématiquement avec les structures afin de faire disparaître le mot-clé struct.
Soit par exemple le type struct ennemi dans un programme :
struct
ennemi{
char
type; //
type
de
l'ennemi
(une
lettre)
float
nuisance; //
potentiel
de
nuisance
float
x,y,px,py; //
position
et
déplacement
int
force; //
indicateur
de
force
pour
les
combats
}
;
En ajoutant le mot-clé typedef devant, on peut définir un synonyme de struct ennemi de la façon suivante :
typedef
struct
ennemi{
char
type;
float
nuisance;
float
x,y,px,py;
int
force;
}
t_ennemi;
et de façon plus concise, on peut définir directement son type de structure :
typedef
struct
{
char
type;
float
nuisance;
float
x,y,px,py;
int
force;
}
t_ennemi;
t_ennemi devient nom de type pour la structure dans les deux cas du fait de l'utilisation de typedef et t_ennemi peut être utilisé partout dans le programme :
#
include
<stdio.h>
#
include
<stdlib.h>
#
include
<time.h>
int
main
(
)
{
t_ennemi E;
int
i;
srand
(
time
(
NULL
));
E=
init_ennemi
(
);
affiche_ennemi
(
E);
return
0
;
}
Le préfixe t_ est pratique. Il permet de préciser qu'il s'agit d'un type et non d'un nom de variable. Cela n'a rien d'obligatoire.
Déclarer ses variables structure en C++
Dans un environnement C++, c’est-à-dire sur un fichier de code .cpp, la définition d’une structure est la même :
struct
entite {
float
x,y;
float
dx,dy;
int
color ;
char
lettre;
}
;
Mais le mot-clé struct est inutile lors de la déclaration d'une structure, il y a en quelque sorte l'utilisation implicite d'un typedef. Au lieu d'écrire comme en C :
struct
entite e1;
nous pouvons écrire directement :
entite e1;
Jusqu'au chapitre sur le C++ nous restons dans cet ouvrage en C pur et d'une façon générale nous supprimons le mot-clé struct à la déclaration d'une structure avec l'utilisation d'un typedef.
I-C. Utiliser une structure▲
I-C-1. Accès aux éléments avec l'opérateur point▲
Toutes les variables d’une structure, c'est-à-dire tous les champs de la structure, sont accessibles via l’opérateur « . » dit « point » de la façon suivante :
struct
pix p;
p.x =
0
;
p.y =
50
;
p.color =
255
;
La première ligne déclare une structure du type struct pix nommée p. Les trois lignes suivantes initialisent chacun des champs de la variable p. Le champ x prend la valeur 0, le champ y prend la valeur 50 et le champ color la valeur 255.
I-C-2. Priorité de l'opérateur point▲
Priorité maximum, tous les autres opérateurs passent après dans l'évaluation d'une expression (cf. Annexe Priorité et associativité des opérateurs).
I-C-3. Une structure comme champ dans une structure▲
L'imbrication de structures est possible. Une structure peut avoir une autre structure comme champ à condition que son type soit connu au moment de la définition de la structure. Par exemple :
typedef
struct
rgb{
//
valeur
de
rouge,
de
vert
et
de
bleu
int
r,g,b; //
pour
avoir
une
couleur
}
rgb;
typedef
struct
coord{
int
x, y; //
pour
stocker
une
position
en
2D
}
coord;
typedef
struct
rectangle{
coord hg; //
coord
du
coin
haut
gauche
coord bd; //
coord
du
coin
bas
droite
rgb color; //
couleur
du
rect
}
rectangle;
La structure rectangle permet de stocker les informations de rectangles colorés. La structure rgb concentre les informations de couleurs avec trois champs, un entier pour le rouge, un entier pour le vert et un entier pour le bleu (en RGB - red, green, blue - une couleur est obtenue par un mélange de rouge, de vert et de bleu). La structure coord permet de stocker les coordonnées d’un point dans un espace à deux dimensions. Pour dessiner un rectangle nous avons besoin de deux points, le point en haut et à gauche et le point en bas et à droite :
La structure finale pour stocker les informations d’un rectangle comprend une structure rgb pour sa couleur et deux structures coord pour chacun des points haut gauche (hg) et bas droite (bd).
Dans cet exemple les structures rgb et coord doivent absolument être définies avant la structure rectangle pour y être connues au moment de sa définition. Dans le cas contraire, il y aura une erreur à la compilation.
L’accès aux champs d’une structure se fait avec l’opérateur . (point) et rien ne change si le champ est lui-même une structure. L’opérateur point sera utilisé une nouvelle fois pour accéder aux champs de la structure élément de la façon suivante :
rectangle r1;
r1.hg.x =
0
;
r1.hg.y =
0
;
r1.bd.x =
100
;
r1.bd.y =
50
;
r1.color.r =
255
;
r1.color.g =
r1.color.b =
0
;
Le type de l’expression r1.hg par exemple est une structure du type coord et on accède à chacun de ses champs en utilisant une nouvelle fois l’opérateur point, soit les expressions e1.hg.x et e1.hg.y et les parenthèses sont inutiles.
I-C-4. Initialiser une structure à la déclaration▲
Une structure peut être initialisée à la déclaration en lui affectant une suite de valeurs entre accolades closes par un point-virgule. Chaque valeur est affectée à un champ correspondant dans l'ordre et doit en respecter le type. Soit la structure test :
typedef
struct
test {
float
x, y;
int
px, py;
char
lettre;
}
test;
et une déclaration suivie d’une initialisation dans le programme :
test t1=
{
1
.5
, 2
.0
, 150
,300
,'
A
'
}
;
Pour la variable t1 de type struct test, 1.5 est affecté au champ x, 2.0 est affecté au champ y, 150 est affecté au champ px, 300 au champ py et le champ lettre prend la valeur 'A'.
Dans le cas de structures imbriquées, il y a le choix entre une seule liste pour les valeurs affectées ou le détail entre accolades pour chaque structure, par exemple :
typedef
struct
date{
int
jour, mois, annee;
}
date;
typedef
struct
time{
int
heure, minute, seconde;
}
time;
typedef
struct
moment{
date date;
time time;
}
moment;
Dans le programme, un exemple de déclaration suivie d’une initialisation :
moment m1=
{
25
,12
,2006
,13
,45
,30
}
;
Les champs de m1 valent respectivement 25 pour jour, 12 pour mois, 2006 pour année, 13 pour heure, 45 pour minute et 30 pour seconde.
On peut également détailler le contenu de chaque structure imbriquée de la façon suivante :
moment m1=
{
{
25
,12
,2006
}
,
{
13
,45
,30
}
}
;
Si le nombre des valeurs de la liste est supérieur au nombre des champs, il y a une erreur de compilation. En revanche, si le nombre des valeurs de la liste est inférieur, les champs restants sont initialisés à 0. Ce test permet de le vérifier :
#
include
<stdio.h>
#
include
<stdlib.h>
struct
date{
int
jour, mois, annee;
}
;
struct
time{
int
heure, minute, seconde;
}
;
struct
moment{
struct
date date;
struct
time time;
}
;
int
main
(
)
{
struct
moment m=
{
{
1
}
, {
3
,4
}
}
;
printf
(
"
m.date.jour=%d\n
"
,m.date.jour);
printf
(
"
m.date.mois=%d\n
"
,m.date.mois);
printf
(
"
m.date.annee=%d\n
"
,m.date.annee);
printf
(
"
m.time.heure=%d\n
"
,m.time.heure);
printf
(
"
m.time.minute=%d\n
"
,m.time.minute);
printf
(
"
m.time.seconde=%d\n
"
,m.time.seconde);
system
(
"
PAUSE
"
);
return
0
;
}
Résultat :
m.date.jour=
1
m.date.mois=
0
m.date.annee=
0
m.time.heure=
3
m.time.minute=
4
m.time.seconde=
0
I-C-5. Copier une structure▲
Les copies et les affectations de structure à structure de même type sont autorisées, par exemple :
coord pt1, pt2;
pt1.x=
100
;
pt1.y=
200
;
pt2=
pt1;
La structure pt1 est initialisée avec les valeurs 100 et 200, ensuite la structure pt1 est affectée à la structure de même type pt2. Les valeurs de pt1 sont recopiées dans pt2.
I-D. Mise en pratique : définir, déclarer, initialiser des structures▲
Exercice 1
Une menuiserie industrielle gère un stock de panneaux de bois. Chaque panneau possède une largeur, une longueur et une épaisseur en millimètres ainsi que le type de bois. Il y a trois types de bois : pin (code 0), chêne (code 1), hêtre (code 2).
- Définir une structure panneau contenant toutes les informations relatives à un panneau de bois ;
- Dans un programme initialiser deux panneaux à la déclaration et deux panneaux avec des valeurs aléatoires. Afficher les valeurs.
Exercice 2
Un grossiste de composants électroniques vend quatre types de produits :
- des cartes mères (code 1) ;
- des processeurs (code 2) ;
- des barrettes mémoire (code 3) ;
- des cartes graphiques (code 4).
Chaque produit possède une référence, qui est un nombre entier, un prix en euros et une quantité disponible. Définir une structure produit qui code un produit et dans un programme initialiser deux produits à la déclaration et deux produits avec des valeurs entrées par l'utilisateur. Afficher les valeurs et cloner le produit le plus cher.
Exercice 3
Soit un programme affichant des flocons de neige qui tombent du haut de la fenêtre de la console. Chaque flocon est une lettre. Donner une structure de données qui permet de coder un flocon. Dans un programme, déclarer quatre flocons. Initialiser un flocon à la déclaration et un autre avec des valeurs aléatoires. Copier chaque flocon et afficher les valeurs des quatre flocons de façon claire.
Exercice 4
Le monde où se trouve le player est parcouru par des ennemis. Donner les caractéristiques essentielles d'un ennemi à imaginer et la structure de données la mieux adaptée. Initialiser un ennemi à la déclaration et un autre avec des valeurs aléatoires. Afficher les valeurs de cet ennemi. Faire un clone.
II. Structures et fonctions▲
II-A. Retourner une structure▲
Comme la copie de structure est autorisée, les structures peuvent être prises comme
valeur de retour. Soit la structure coord :
typedef
struct
coord{
int
x, y;
}
coord;
Voici typiquement une fonction qui permet d'initialiser une structure après sa déclaration n'importe où dans le programme :
coord init_point1
(
)
{
coord t0;
t0.x =
rand
(
)%
1024
;
t0.y =
rand
(
)%
768
;
return
t0;
}
Dans la fonction :
- une structure du type souhaité est déclarée ;
- chacun de ses champs est initialisé avec des valeurs aléatoires ou calculées ;
- pour finir, la structure résultante est retournée au contexte d'appel.
Bien sûr des valeurs peuvent être transmises en paramètres, cela donne par exemple :
coord init_point2
(
int
x, int
y)
{
coord t0;
t0.x =
x;
t0.y =
y;
return
t0;
}
Et dans le programme la structure retournée par la fonction est affectée à une structure du contexte d’appel :
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
(
)
{
coord pt1, pt2;
pt1 =
init_point1
(
);
pt2 =
init_point2
(
40
, 40
);
printf
(
"
pt1.x=%d,
pt1.y=%d\n
"
"
pt2.x=%d,
pt2.y=%d\n
"
,pt1.x,pt1.y,pt2.x, pt2.y);
return
0
;
}
L'appel de init_point1() renvoie une structure coord initialisée avec des valeurs aléatoires et elle est affectée à la variable coord pt1. L'appel de init_point2() renvoie une autre structure coord initialisée avec (40,40) et affectée à la variable coord pt2.
II-B. Structures en paramètre de fonction▲
Une structure est passée à un paramètre de fonction de même type par valeur comme n’importe quel autre type de variable. La structure passée est copiée dans le paramètre de la fonction.
Attention !
Soit par exemple la fonction ci-dessous, elle permet de faire progresser de 1 la position horizontale d’un point. Si la position dépasse 1000, elle retourne à 0 :
void
modif
(
coord pt)
{
pt.x+
+
;
if
(
pt.x>
1000
)
pt.x=
0
;
}
Dans un programme l'appel est le suivant :
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
(
)
{
coord p=
{
10
,20
}
;
printf
(
"
p.x=%d,
p.y=%d\n
"
, p.x, p.y);
modif
(
p);
printf
(
"
p.x=%d,
p.y=%d\n
"
, p.x, p.y);
return
0
;
}
Qu'imprime le programme ?
p.x=
10
, p.y=
20
p.x=
10
, p.y=
20
Il n'y a pas de modification de la structure p. La structure en paramètre de la fonction modif() est locale à la fonction. C'est une autre variable. Au moment de l'appel, l'affectation implicite suivante est réalisée :
modif
(
pt =
p);
Mais à l'issue de la fonction, la structure p dans le contexte d'appel n'a pas été touchée. Pour avoir une fonction de modification des valeurs contenues dans une structure, il faut passer la structure à modifier en argument et retourner la structure modifiée, ce qui donne :
coord modif
(
coord pt)
{
pt.x+
+
;
if
(
pt.x>
1000
)
pt.x=
0
;
return
pt;
}
avec l'appel :
int
main
(
)
{
coord p=
{
10
,20
}
;;
printf
(
"
p.x=%d,
p.y=%d\n
"
, p.x, p.y);
p=
modif
(
p);
printf
(
"
p.x=%d,
p.y=%d\n
"
, p.x, p.y);
return
0
;
}
Cette fois la copie modifiée dans la fonction est affectée à la structure p locale au
main() et le programme imprime :
p.x=
10
, p.y=
20
p.x=
11
, p.y=
20
II-C. Mise en pratique : structures et fonctions▲
Exercice 1
Une menuiserie industrielle gère un stock de panneaux de bois. Chaque panneau possède une largeur, une longueur, une épaisseur en millimètres, un volume et un type de bois. Il y a trois types de bois : pin (code 0), chêne (code 1), hêtre (code 2).
- Définir une structure panneau contenant toutes les informations relatives à un panneau de bois ;
- Écrire les fonctions de saisie d'un panneau de bois ;
- Écrire une fonction d'affichage d'un panneau de bois ;
- Écrire une fonction qui calcule le volume en mètre cube d'un panneau ;
- Écrire une fonction qui affiche le volume d'un panneau de bois.
Exercice 2
Un grossiste de composants électroniques vend quatre types de produits :
- des cartes mères (code 1) ;
- des processeurs (code 2) ;
- des barrettes mémoire (code 3) ;
- des cartes graphiques (code 4) ;
Chaque produit possède une référence, qui est un nombre entier, un prix en euros et une quantité disponible.
- Définir une structure « produit » qui code un produit ;
- Écrire une fonction de saisie des données d'un produit ;
- Écrire une fonction d'affichage des données d'un produit ;
- Écrire une fonction qui permet à un utilisateur de saisir la commande d'un produit.
L'utilisateur identifie le produit et saisit la quantité demandée. L'ordinateur affiche toutes les données de la commande y compris le prix.
Exercice 3
Soient les deux types de structure « date » et « personne » :
typedef
struct
date{
int
jour, mois, annee;
}
date;
typedef
struct
personne{
int
identifiant;
struct
date date_embauche;
struct
date date_poste;
}
personne;
- Écrire une fonction pour initialiser une structure de type « personne ».
- Écrire une fonction d'affichage de façon à obtenir soit l'un soit l'autre des deux affichages
ci-dessous :
Identifiant : 1224
date embauche (jj mm aa) : 16 01 08
date poste = date embauche ? (O/N) : O
Identifiant : 1224
date embauche (jj mm aa) : 16 01 08
date poste = date embauche ? (O/N) : N
date poste (jj mm aa) : 01 09 08
Exercice 4
Soit une entité dans un jeu vidéo en mode console. Elle est définie par une position, un déplacement, un type (rampant, grouillant, serpentant, plombant, assommant), une couleur. L'entité a également un nom (une lettre) qui sert pour son apparence et une série de taux : taux d'agressivité, de colère, de convoitise, de faim, de peur.
Définir la structure de données pour coder une entité. Écrire une fonction d'initialisation, une fonction de mise à 0 (reset) et une fonction d'affichage. Tester dans un programme avec un menu : quitter, afficher, initialiser, reset.
II-D. Expérimentation▲
L'intérêt des structures est de permettre de regrouper en une seule variable plusieurs variables. Cette propriété donne aux fonctions la possibilité d'innombrables paramètres et valeurs de retour. En recevant une structure en argument la fonction reçoit en une fois un ensemble de valeurs, de même en retournant une structure la fonction retourne en une fois un ensemble de valeurs. Il devient possible de gérer de plus gros ensembles de variables et ça commence à donner de la force aux programmes.
II-D-1. Fourmi 1 : une fourmi mobile à l’écran▲
L'objectif est de faire circuler une fourmi à l'écran. Elle ne fait que bouger, rien d'autre.
Elle ne se nourrit pas et ne rencontre aucune autre bestiole.
Précisions sur les paramètres et les retours des fonctions
La fonction init() utilise uniquement le mécanisme de retour : une structure fourmi est initialisée dans le corps de la fonction et juste avant de disparaître de la mémoire, elle est recopiée dans une autre en dehors de la fonction, la fourmi qui est déclarée dans le main et qui demeure en permanence jusqu'à la fin du programme.
La fonction avance() utilise les deux mécanismes, une fourmi en paramètre d'entrée et une fourmi en valeur de retour à la sortie. Au moment de l'appel une fourmi du contexte d'appel est recopiée dans le paramètre de type fourmi. Dans le corps de la fonction, les instructions sont exécutées sur la copie et donc la fourmi du contexte d'appel n'est pas modifiée. Pour récupérer cette copie qui est modifiée à l'intérieur de la fonction, la fourmi du contexte d'appel doit recopier cette copie modifiée de la fonction lorsqu'elle est retournée. D'où l'expression :
f =
avance
(
f);
La fonction affiche() n'utilise qu'un paramètre fourmi. En effet la fonction a besoin des informations de la fourmi à afficher, mais elle ne les modifie pas, ainsi la copie dans le paramètre suffit et il n'y a rien à retourner :
affiche
(
f, f.color)
La fonction top() utilise un pointeur et une technique qui est présentée au chapitre Les pointeurs, mais le principe ici n'est pas très compliqué. Un pointeur est une variable qui prend pour valeur une adresse mémoire. En paramètre ça permet de donner l'adresse d'une variable à la fonction et dans le corps de la fonction de pouvoir écrire à cette adresse mémoire (grâce à l'opérateur étoile avec l'expression *start dans la fonction). Cette utilisation des pointeurs permet de transformer le paramètre d'entrée en une entrée+sortie. C'est utile lorsque plusieurs retours sont nécessaires comme dans cette fonction. Le retour classique de type int est utilisé pour le résultat de la fonction ce qui permet de l'appeler directement dans le test :
if
(
top (
&
start, 150
) ){
}
Lors de l'appel de la fonction, c'est l'adresse de la variable start qui est passée au pointeur de même nom, soit l'expression :
&
start
Dans la fonction le paramètre pointeur int*start est utilisé afin de pouvoir modifier éventuellement la valeur de la variable start du contexte d'appel si le temps demandé est passé.
II-D-2. Voiture 2 : structure circuit, structure voiture▲
Grâce aux structures nous pouvons reprendre le programme voiture 1 présenté précédemment et lui ajouter une course à quatre voitures. Rappelons que dans la première version pour une voiture il faut sept variables avec des fonctions dédiées à ces variables déclarées en globale.
void
select_trait (
void
);
void
avancer (
int
l, int
color1, int
color2);
Avec quatre voitures, il faut donc 28 variables et des fonctions dédiées pour chaque voiture ou des fonctions généralisées avec un nombre très élevé de paramètres.
En revanche avec un type de structure voiture nous avons quatre voitures, chaque voiture est un ensemble de sept variables. Les mêmes fonctions servent pour chaque voiture. Il suffit de passer la voiture concernée en paramètre, et si des modifications sont opérées, de récupérer la voiture modifiée en valeur de retour.
Comme il y a plusieurs voitures, il faut arrêter la course dès qu'une voiture gagne et le test de la boucle principale doit être modifié. De plus l'initialisation des voitures vaut la peine aussi d'une fonction à part qui permet de donner à chacune des valeurs différentes de façon simple, aisément modifiable dans le code.
L'algorithme est toujours le même que celui du programme voiture 1 vu précédemment. Simplement l'utilisation de structure permet d'avoir plusieurs voitures facilement. Pour avoir 10 voitures, il suffit de déclarer 10 struct voiture, de les initialiser et dans la boucle avoir une fois pour chaque voiture v1,v2…v10 le bloc :
if
(
top
(
v1)){
v1.tmps=
clock
(
);
if
(
v1.pas=
=
0
)
v1=
select_trait
(
v1,c);
v1=
avancer
(
v1);
}
Généralisation d'un bloc d'instructions en une fonction
Ce bloc est répété pour chaque voiture. Pour fonctionner, il a besoin d'une voiture et d'un circuit. Sur les deux paramètres, un seul change, c'est la voiture. Alors il faut le généraliser, c'est-à-dire transformer en une fonction qui prend en paramètres une voiture et un circuit, et comme le paramètre voiture peut être modifié, il est retourné en sortie. C'est la fonction :
voiture bouge (
voiture v, circuit c)
{
if
(
top
(
v)){
v.tmps=
clock
(
);
if
(
v.pas=
=
0
)
v=
select_trait
(
v,c);
v=
avancer
(
v);
}
return
v;
}
Au moment de l'appel, il suffira de préciser de quelle voiture et quel circuit il s'agit.
v1 =
bouge
(
v1, c);
v2 =
bouge
(
v2, c);
v3 =
bouge
(
v3, c);
v4 =
bouge
(
v4, c);
Comme il y a plusieurs voitures, la course prend fin si une voiture a terminé. Le test aurait pu être mis tel que dans la boucle principale. Mais c'est plus propre d'écrire une fonction qui retourne le résultat. Notre objectif maintenant est de faire tourner 100, 1000 ou 10 000 voitures. Clairement les seules structures ne suffisent plus et il faut leur adjoindre des tableaux, il faut faire des tableaux de structures.
III. Conclusion▲
Ce tutoriel est extrait du livre Du C au C++ - De la programmation procédurale à l'objet de Frédéric Drouillon.
Commandez Du C au C++ - De la programmation procédurale à l'objet sur Amazon
IV. Remerciements▲
Je tiens sincèrement à remercier Vincent VIALE pour la mise au gabarit et Claude LELOUP pour la relecture orthographique.