Académique Documents
Professionnel Documents
Culture Documents
Programmation
Équipe pédagogique
1 Introduction 5
I PROGRAMMATION
C++ 6
2 Données et structures de données 8
2.1 Les données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.1 Les types simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.2 Les variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.3 Les constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Les types composés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.1 Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.2 Les enregistrements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.3 Les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.4 Les listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2.5 Les arbres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3 Structure de programme 17
3.1 Forme générale d’un programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1 Ligne de commentaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.2 Les expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.3 Les instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.1.4 Structure du texte de la fonction principale . . . . . . . . . . . . . . . . . . . 18
3.2 Les instructions simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.1 Instruction d’a↵ectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.2 Lecture et écriture en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3 Les structures d’instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3.1 Les structures de choix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3.2 Les structures répétitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2 Exemple de rapport 56
2.1 Dossier algorithmique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.1.1 Algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.1.2 Traduction en C/C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
III ANNEXE 65
Ce polycopié a été élaboré avec l’aide des enseignants des équipes pédagogiques
successives de l’enseignement d’Algorithmique et Programmation.
Introduction
PROGRAMMATION
C++
En C++, toute donnée (constante ou variable) doit être déclarée avant d’être uti-
lisée. Les instructions de déclaration doivent être placées au début de l’unité qui utilise ces
variables, avant toutes les autres instructions.
Les informations sur la taille et la valeur sont données pour un système 32 bits, mais la
précision est au moins respectée sur un système plus récent.
Les chaı̂nes de caractères utilisent le type string. Pour cela, il faut inclure la librairie
string pour gérer les chaı̂nes de caractères avec le code suivant en début de fichier (avant
d’utiliser le type éponyme string) :
#include <string>
Remarque : Les identifiants sont sensibles à la casse : vitesse, viTeSSe et vITESSE sont
trois identificateurs di↵érents.
Remarque : Portée d’une déclaration : les instructions de déclaration doivent être placées au
début du bloc d’instruction qui utilise ces variables, ou dans un bloc d’un niveau supérieur,
et par convention avant toutes les autres instructions.
Une constante littérale est une chaı̂ne de caractères constante. Elle est formée d’une
suite de caractères quelconques délimitée par des guillemets qui ne font pas partie de
la valeur du littéral. Les littéraux apparaissent principalement dans des écritures pour
constituer des messages émis par le programme.
Exemple :
"CECI EST UN LITTERAL" /* La constante CECI EST UN LITTERAL */
Remarque : les valeurs des constantes ne sont en général pas données en algorithmique, mais
sont obligatoirement spécifiées en C++.
Exemples :
algorithme
type
t_VectEnt : vecteur d’entiers
t_MatReels : matrice de réels
t_Chaine : vecteur de caractères
variables
t_VectEnt a
Vecteur d’entiers b
C++
Pour un vecteur, un élément est repéré par son indice placé entre crochets, après le nom
du vecteur.
Exemples :
(les types des variables étant supposés compatibles) :
v[i]=a ; a=v[i+2] ; v[i] = v[i+2] ;
Pour une matrice, chaque élément doit être repéré par deux indices, chacun étant placé
entre crochets à la suite du nom de la matrice.
Exemple :
M[i][j] = 0.2 ;
Pour un tableau de dimension supérieure à 2, chaque élément doit être repéré par autant
d’indices placés entre crochets à la suite du nom du tableau que le tableau a de dimensions.
Exemple :
T[2][J][J+1]=5 ;
Attention : En interne, le langage C++ gère un tableau comme un pointeur constant sur
le premier élément. Cela explique un certain nombre de restrictions sur leur utilisation que
nous détaillerons dans la suite.
⇤ Chaı̂nes de caractères
Une chaı̂ne de caractères est une suite de caractères représentant, en général, un texte.
Par exemple, "Bonjour" est une constante de type chaı̂ne de caractères (on parle aussi de
constante littérale).
Le langage C++ ne définit pas de type « chaı̂ne de caractères » à proprement parler : une
chaı̂ne de caractères est définie en C++ comme un tableau de caractères à une dimension.
La taille du tableau n’est pas importante mais le dernier caractère doit être le caractère \0.
Pour définir un type chaı̂ne de caractères t_Chaine on pourra ainsi écrire :
C++
typedef char t_Chaine[256]; // Un tableau de caractères de taille 256
De nombreuses fonctions de manipulation des chaı̂nes de caractères sont disponibles en
incluant la bibliothèque string. On y trouve notamment la définition d’un type chaı̂nes de
caractères (string), des fonctions pour copier des chaı̂nes de caractères et les concaténer.
C++
Exemple :
struct NomDuType struct t_Article
{ {
(type1) champ1; int reference;
(type2) champ2; string nom;
}; };
On peut, bien sûr, créer des types composés de types composés tels que des tableaux
d’enregistrements par exemple.
Exemple :
algorithme
variables
t_Article artCour
t_VectArticle mesArticles
C++
t_Article artCour; // artCour est de type t_Article
t_VectArticle mesArticles; // vecteur d’éléments de type t_Article
1. Ou d’une constante, mais on se concentrera sur les variables dans cette partie.
a
(de type int)
1001
(Valeur de p)
p
(Pointeur sur a de type int*)
Figure 2.1 – Illustration d’un pointeur p de type int * sur un entier a. Le pointeur a pour
valeur l’adresse de a.
Afin de pouvoir donner des valeurs à nos pointeurs, le langage C++ propose également
une construction pour récupérer l’adresse d’une variable donnée : si a est une variable alors
&a est l’adresse mémoire de a.
Enfin, on peut accéder au contenu d’une variable par l’intermédiaire d’un pointeur sur
cette variable. On parle de déréférencement du pointeur : si p est un pointeur sur une
variable a alors *p est le contenu de la variable a.
C++
int a; // une variable entière
int *p; // un pointeur sur une variable entière
p = &a; // p pointe sur a
*p = 2; // a vaut maintenant 2
Un pointeur est une variable qui contient l’adresse d’une autre variable. Nous pouvons
allouer manuellement une case dans la mémoire pour stocker la valeur d’une variable. Pour
cela il faut utiliser l’opérateur new. new demande une case à l’ordinateur et renvoie un
pointeur pointant vers cette case.
Par exemple :
C++
int *p;
p = new int;
Ici il y aura deux cases mémoire utilisées. La deuxième ligne demande une case mémoire
pouvant stocker un entier et l’adresse de cette case est stockée dans le pointeur qui a été
déclaré à la première ligne.
Nous verrons cependant comment les pointeurs peuvent être utilisés pour contourner les
limitations du langage C++ en termes de valeurs de retour des fonctions, ou pour définir
des types récursifs.
Dans le cas où on dispose de pointeurs sur des enregistrements, l’accès peut se faire
par l’utilisation de l’opérateur de déréférencement * puis le ., mais le langage propose un
opérateur plus simple noté ->.
Exemple :
t_Article *ptrArticle;
...
// accès avec le déréférencement du pointeur
(*ptrArticle).reference = 42;
...
// accès avec utilisation directe du pointeur
ptrArticle->reference = 23;
ptrArticle->libelle = "stylo";
Nous allons nous intéresser au cas particulier des listes chainées, par exemple d’entiers.
Chaque élément de la liste est composé de :
– une valeur (une étiquette) ; dans l’exemple, c’est un entier,
– un pointeur vers l’élément suivant. Cas particulier du dernier élément de la liste où le
lien ne pointe vers rien. En C++, elle pointe vers un élément NULL.
algorithme
C++
struct t_ListeEntier
{
// type de la donnée : un entier
int valeur;
// pointeur sur l’élément suivant
t_ListeEntier* suivant;
};
type
t_arbreCar : Enregistrement
caractères valeur,
t_arbreCar filsGauche, // pointe sur le fils gauche
t_arbreCar filsDroit, // pointe sur le fils droit
Fin_enregistrement
C++
struct t_arbreChar
{
// type de la donnée : un caractère
char valeur;
// pointeur sur le fils gauche
t_arbreChar* filsGauche;
// pointeur sur le fils droit
t_arbreChar* filsDroit;
};
Structure de programme
Remarque importante : Attention, dans une condition, l’écriture (a=b) est interprétée
comme l’a↵ectation à a de la valeur de b, et le résultat de l’évaluation de cette condition est
vrai (6= 0) si cette valeur n’est pas nulle. Ne pas la confondre avec la comparaison (a==b).
int main () {
// Déclarations des variables et constantes locales
........................................ ;
........................................ ;
// corps de la fonction principale : instructions exécutables et commentaires
........................................ ;
........................................ ;
// On renvoie 0 pour indiquer que le programme s’est bien terminé
return 0 ;
}
Remarque importante : si les variables sont de type tableau, l’a↵ectation doit se faire
élément par élément.
C++
SX = sin(A + 3.*Y) - Y/(Z - 1.41);
Les 4 opérateurs arithmétiques (+ - * /) sont identiques en Algorithmique et en C++.
De plus, le modulo existe en C++, il est noté %.
Remarque importante : attention aux types des opérandes et donc au résultat final, si n
et m sont de type entier, / désigne la division entière et n/m est un entier.
int a, b;
float c;
c = 2/3; // c = 0, division entière
c = 2/3.; // 3 est un float pour cette opération : c = 0.666667
c = 2./3; // 2 est un float pour cette opération : c = 0.666667
a = 2;
b = 3;
c = a/b; // c = 0, division entière
c = (float)a/b; // a est un float pour cette opération : c = 0.666667
// (float) est un transtypage explicite
Exemple :
algorithme
mesArticles[1] artCour ;
v1 v2 ; /* vecteur d’entiers */
C++
mesArticles[1] = artCour ; /* affectation globale */
C++
cin >> uneVariable; // lecture d’une valeur au clavier
cin >> var1 >> var2 >> var3; // lecture de 3 valeurs
Exemple pour des variables de types composés :
algorithme
{mesArticles(1), v2} lire ()
C++ :
// lecture champ par champ de mesArticles[1]
cin >> mesArticles[1].reference
>> mesArticles[1].libelle
>> mesArticles[1].prix;
algorithme
écrire (var1,var2,var3)
écrire ("la racine de x" + r)
C++
// affichage des valeurs de 3 variables (pas forcément de m^eme type)
// on note que les valeurs seront pas séparées par un espace
cout << Var1 << Var2 << Var3 << endl;
// composition de différentes sorties
cout << "la racine est x=" << r << endl;
// pour aller à la ligne, utilisation de endl (end line)
algorithme
écrire ((mesArticles(1), v2))
C++ :
// écriture champ par champ d’un "article"
cout << mesArticles[1].reference << " "
<< mesArticles[1].libelle << " "
<< mesArticles[1].prix << endl;
Remarques :
– En écriture si le fichier donnees.txt n’existe pas, il est créé.
– En lecture si le fichier donnees.txt n’existe pas ou s’il n’est pas dans le répertoire
d’exécution du programme, il y aura une erreur à l’exécution.
– Comme pour les écritures sur le terminal, il est possible de mettre des « passages à la
ligne » dans les fichiers :
monFichier<<endl ; // passage à la ligne dans monFichier, donc dans don-
nées.txt
– Le booléen monFichier.eof(), qui est mis à vrai quand une lecture est tentée après
la fin de fichier, permet de tester si le curseur est arrivé en fin de fichier.
/* ==============================================================================
O. Roux Departement I & M
Programme LireEcrire 02-07-02
---------------------------------------------------------------------------------
BUT DU PROGRAMME : ILLUSTRER L’UTILISATION DE FICHIERS EN LECTURE/ECRITURE EN C++
Une phrase est placee dans le fichier resultat.dat dont on lit ensuite le contenu
============================================================================== */
int main () {
// DECLARATION DES VARIABLES
string uneChaine;
fstream fichier,out;
if (P)
if (A <= B)
{
{
// liste d’instructions
xMax = B;
}
xMin = A;
}
Si P est vraie, la suite d’instructions instructions est réalisée, si P est fausse, on
sort directement de la structure pour poursuivre avec l’instruction qui suit l’accolade
fermante } .
if (A > B)
if (P) {
{ xSup = A;
// liste d’instructions xInf = B;
} }
else else
{ {
// autre liste d’instructions xSup = B;
} xInf = A;
}
Si P est vraie , instructions 1 est réalisée, puis on sort de la structure pour pour-
suivre avec l’instruction suivant la dernière } de cette instruction conditionnelle.
Si P est fausse, c’est directement instructions 2 qui est réalisée, puis on sort de la
structure pour poursuivre avec l’instruction suivant la dernière } de cette instruction
conditionnelle.
où var est une variable de type entier ou caractère et val1, val2, . . . , valN des
valeurs possibles de cette variable. Dans l’exemple, ind est de type entier.
C++
switch (var) Exemple :
{
case val1:
// instructions switch(ind)
break; {
case val2: case 1: cout << "2 racines reelles" << endl;
// instructions break;
break: case 2: cout << "2 racines complexes" << endl;
... break;
default: default: cout << "1 racine double" << endl;
// instructions }
}
Remarques :
– L’instruction break permet d’aller à l’instruction suivant l’accolade fermante termi-
nant le bloc switch.
– Si dans l’algorithme, les conditions de choix ne sont pas du type var=vali avec var
de type entier ou caractère, il faut traduire la structure algorithmique par des if
...else imbriqués.
C++
Exemple :
cin >> n;
for (i=iDeb;i<=iFin;i=i+iPas) som=0;
{ for (i=1;i<=2*n;i=i+2)
// instructions {
} som=som+i;
}
Remarque : l’instruction for est plus générale que la structure algorithmique ”pour”.
Sa syntaxe est : for (initialisation ; condition ; incrémentation) où :
– initialisation permet d’initialiser la boucle,
– condition en devenant fausse permet de terminer la boucle,
⇤ L’instruction while
Elle traduit la structure itérative ”tant que ... faire / fin tant que ”. C’est la
structure itérative la plus générale.
algorithme
Exemple :
som 0
instructions 1 //initialisations lire ((x))
tant que P faire tant que x>0 faire
| instructions 2 | som som+x
fin tant que | écrire ((som))
| lire ((x))
fin tant que
C++
Exemple :
som=0;
// instructions 1 cin >> x;
while (P) while (x>0)
{ {
// instructions 2 som=som+x;
} cout << "somme " << som << endl;
cin >> x;
}
Programmes et sous-programmes
C/C++ : tout est fonction !
Toute fonction, qu’il s’agisse de la fonction principale ou non, possède la même structure.
Un fichier source contenant des fonctions C/C++ se présente de la manière suivante :
– des commentaires de présentation,
– des directives éventuelles d’inclusion de fichiers, placées après ces commentaires,
– La définition d’une ou plusieurs fonctions contenant :
– le prototype de la fonction :
unType nomDeFonction(liste-paramètres)
– le corps de la fonction entre { et } ,
– s’il y a lieu, une instruction return suivie du résultat renvoyé par la fonction.
Notez que la fonction principale, de nom réservé main, est nécessairement de type int
(cf. section 3.1.4, p.18).
L’instruction return permet de renvoyer à la fonction appelante au plus une seule valeur
de type (noté ici unType) scalaire ou enregistrement. Si la fonction appelée ne doit rien
renvoyer à la fonction appelante par ce biais, elle est déclarée de type void et ne comporte
pas d’instruction return.
Attention : pour des raisons de clarté et de facilité de vérification du code écrit, il est
demandé de n’écrire qu’au plus une instruction return par fonction et de l’écrire en dernière
instruction. En e↵et, cette instruction arrête le déroulement de la fonction : toute instruction
écrite après ne serait pas exécutée.
Un paramètre identifie un objet d’une fonction et est destiné à être associé à un argument
lors de l’activation du programme. Il doit donc apparaı̂tre dans la liste des paramètres du
prototype de la fonction et être normalement déclaré dans cette liste, par son type et son
nom. Ce paramètre ne peut être qu’un nom de :
– variable non indicée, de type simple ou composé,
– fonction, ce cas n’est pas étudié dans ce polycopié.
– un bloc d’instructions, si elle est déclarée à l’intérieur d’un tel bloc, délimité par des
accolades.
Toute variable définie à l’intérieur d’une fonction sera connue localement dans cette
fonction à partir de sa déclaration et jusqu’à l’accolade fermante correspondant à l’accolade
ouvrante après laquelle elle a été déclarée.
La portée d’un paramètre est la fonction dont il est paramètre.
En C++, un argument peut être passé par référence. Il n’y a qu’un seul objet en mémoire,
l’argument de la fonction appelante, connu sous deux noms éventuellement di↵érents :
– son nom en tant qu’argument, utilisé dans la fonction appelante,
– son nom en tant que paramètre, utilisé dans la fonction appelée.
L’association entre l’argument et le paramètre permet alors, en modifiant la valeur du
paramètre dans la fonction appelée, de modifier la valeur de la variable définie dans la
fonction appelante. En C++ le passage par référence d’un argument est noté par un &
placé entre le type et le nom du paramètre correspondant.
En C++, l’utilisation des références permet d’accéder au contenu d’une case mémoire
donnée en utilisant plusieurs ”alias” sur cette case mémoire, plusieurs références. Voyons un
exemple dans un programme principal qui afficherait le contenu d’une variable soit directe-
ment en passant par la variable soit en utilisant une référence sur cette variable.
C++
#include <iostream>
using namespace std;
int main()
{
int age = 20; //Une variable pour contenir un entier
return 0;
}
L’exécution de ce code donne :
Vous avez 20 ans. (via variable)
Vous avez 20 ans. (via reference)
Cet exemple est un peu artificiel. Le passage par référence va surtout nous permettre de
modifier les valeurs des variables passées en paramètres d’une fonction et ainsi permettre à
une fonction écrite en C++ de ”renvoyer” plusieurs résultats (voir 4.3.2 pour un exemple).
Attention : un argument de type tableau est automatiquement passé par adresse en C++.
C’est l’adresse mémoire de son premier élément (celui d’indice(s) 0) qui est passé à la fonction
appelée, concrètement, cela signifie qu’un tableau sera modifiable par toute fonction auquel
il sera passé en argument.
4.2.1 Exemples
algorithme
fonction nomDeLaFonction Exemple :
spécification : fonction somme
fonction : {. . . } nomDeLaFonction(. . .) spécification :
paramètres : . . . fonction : s somme(a,b)
résultats : ... paramètres : entier a, b
début
résultats : entier s
// corps de la fonction début
... / corps de la fonction /
/*valeur à retourner de type simple, ou s a+b
enregistrement, si res est cette valeur :*/ renvoyer s
renvoyer res fin
fin
C++
Exemple :
/type/ nomDeLaFonction(ListeParametres)
{
/* fonction renvoyant la somme entiere
/* déclarations internes à la fonction */
de ses deux parametres entiers */
... ;//dont res du type renvoyé
/* corps de la fonction */ int somme (int a, int b)
... ; {
/* si res est la valeur à retourner */ int s;
return res ; s = a + b;
} return s;
}
type
t_VectEnt : vecteur d’entiers
fonction sommeVectEnt
problème : Faire la somme des n premières valeurs d’un vecteur d’entier passé en
paramètre.
spécification :
fonction : s sommeVectEnt(D,n)
paramètres : t VectEnt D, entier n
résultats : entier s
aurait pour prototype (si le type t VectEnt est déclaré par ailleurs) :
int sommeVectEnt(t_VectEnt D, int n)
Exemple :
en utilisant les fonctions somme et sommeVectEnt déclarées ci-dessus :
algorithme
p n*somme(2,m) //n, m et p de type entier, utilisation dans une
expression
score1 sommeVectEnt(D1, nb) //D1 de type t_VectEnt et nb entier
C++
p = n*somme(2,m);
score1 = sommeVectEnt(D1,nb);
⇤ Exemples
Exemple 1 : pour traduire l’algorithme de la fonction trierListeArticles(listeA,nbA) qui
renvoie le tableau d’articles listeTriee, on pourra :
– soit créer un nouveau type d’enregistrement contenant un seul champ de type
t VectArticles, et se ramener au cas précédent (section 4.2, p.33),
– soit modifier le prototype de la fonction en ajoutant un paramètre listeTriee. Les
arguments listeA et nbA ne devront pas être modifiés alors que listeTriée devra l’être.
Le prototype de la fonction trierListeArticles s’écrira :
void trierListeArticles(t_VectArticles listeA, int nbA, t_VectArticles listeTriee)
où le tableau listeTriee est passé par adresse, donc modifiable. Cette écriture peut aussi
être utilisée en C++. listeA qui est aussi un tableau pourra aussi être modifiée.
C++
⇤ Exemples
Exemple 1 : pour traduire l’algorithme de la fonction trierListeArticles(listeA,nbA) qui
renvoie le tableau d’articles listeTriee, s’il n’est pas nécessaire de garder l’ancien vecteur
d’articles, il est possible de modifier directement l’argument correspondant au paramètre
listeA. Dans ce cas, il n’est pas nécessaire d’ajouter un nouveau paramètre, le prototype de
la fonction trierListeArticles s’écrira :
void trierListeArticles({t_VectArticles listeA,int nbA)
où le tableau listeA est passé par adresse.
Attention : dans ce cas la traduction du corps de l’algorithme de la fonction demande en
général une adaptation, notamment au niveau des initialisations.
C++
trierListeArticles(listeArticles, nbArt);
miseAJour(monArticle, monPrix);
miseAJour(listeArticles[i], sonPrix);
Exemple 3 : Échange de deux valeurs entières en utilisant une sous fonction echanger
et le passage par référence de ses paramètres.
C++
#include <iostream>
using namespace std;
int main(){
// déclaration de deux variables entières
int a,b;
// lecture au clavier des valeurs des deux entiers
cin>>a>>b;
// écriture à l’écran de ces valeurs
cout<< a <<" "<<b<<endl;
// échange des deux valeurs
echanger(a, b);
// écriture à l’écran de ces valeurs après échange
cout<< a <<" "<<b<<endl;
return 0;
}
10 20
20 10
Remarque : Pour échanger deux valeurs, au lieu d’utiliser les références aux entiers passés
en paramètres nous aurions pu utiliser des pointeurs dans la fonction echanger.
C++
#include <iostream>
using namespace std;
int main(){
// déclaration de deux variables entières
int a,b;
// lecture au clavier des valeurs des deux entiers
cin>>a>>b;
// écriture à l’écran de ces valeurs
cout<< a <<" "<<b<<endl;
// échange des deux valeurs en passant à echanger les adresses mémoire des
variables a et b
echanger(&a, &b);
// écriture à l’écran de ces valeurs après échange
cout<< a <<" "<<b<<endl;
return 0;
⇤ Cas simples
Si la fonction renvoie une seule valeur de type UnType simple ou enregistrement, il faut
utiliser une fonction ayant pour type UnType.
Si la fonction, dans sa spécification algorithmique, ne renvoie pas de valeur, il faut
utiliser une fonction de type void.
Exemples :
Remarque : les constantes, types de données, éventuellement flots de données, utilisés dans
tous les éléments d’un programme sont définis dans le fichier en-tête correspondant à la
fonction principale (cf. exemple ci-après), ce fichier sera ensuite inclus dans tous les fichiers
contenant des fonctions utilisant tout ou partie de ces éléments.
Instructions de compilation
Les di↵érentes phases sont :
– compilation : transformer un fichier .cxx en un fichier .o
– édition des liens : rassembler un ensemble de fichiers .o en un fichier .out
Supposons que nous ayons les fichiers suivants : f1.cxx, f2.cxx, f1.h, f2.h et main.cxx. La
phase de compilation va nous permettre d’obtenir les fichiers.o correspondants aux di↵érents
fichiers .cxx :
– avec la commande
g++ -c f1.cxx
on obtient f1.o
– avec la commande
g++ -c f2.cxx
on obtient f2.o
– avec la commande
g++ -c main.cxx
on obtient main.o
Ensuite l’étape d’édition de liens va nous permettre d’obtenir un programme exécutable.
La commande
g++ -o machin.out main.o f1.o f2.o
nous permet d’obtenir l’exécutable machin.out qui sera exécuté car la commande
./machin.out
Fichiers ”Makefile”
L’ objectifs des fichiers Makefile est d’automatiser les phases de compilation et d’édition
de liens.
On a encouragé la multiplication des fichiers source, d’où l’intérêt d’automatiser la
compilation ; si on a n fichiers sources, on a également n fichiers objet et toujours un seul
exécutable. On utilise de nombreux fichiers sources pour pouvoir travailler à plusieurs sur le
même code, voire pour rendre les fichiers sources eux-mêmes plus lisibles. Un autre intérêt
est que la compilation de petits fichiers source est beaucoup plus rapide que la compilation
de très gros fichiers. Dans le cas des Makefile, on ne va recompiler que le strict nécessaire.
Principe
Syntaxe du Makefile
Ces règles sont écrites dans un fichier qui s’appelle obligatoirement Makefile avec un M
majuscule et le reste en minuscules.
La régénération de la cible est faite en appelant la commande make. Le fichier commence
par la cible principale (dans notre cas, c’est toujours le fichier exécutable).
# on commence toujours par la cible principale (ici un executable)
machin.out: main.o ff1.o ff2.o
g++ -o machin.out main.o ff1.o ff2.o
ff2.o: ff2.cxx
g++ -c ff2.cxx
C++
La définition de ce type se fera dans un fichier à en-tête que nous appellerons listeEntier.h
/**
* Structure de donnees de liste d’entiers
*/
struct t_ListeEntier {
// type de la donnee : un entier
int valeur;
fonction creeElementListe
spécification :
fonction : {nouvelElement} creeElement(x)
paramètres : entier x
résultats : t ListeEntier nouvelElement
algorithme
variables
t ListeEntier nouv
début
| nouv.valeur x
| nouv.suivant 0
| renvoyer nouv
fin
#include "listeEntier.h"
t_ListeEntier* creeElement(int x) {
// variable locale
t_ListeEntier *nouv;
// réservation de la mémoire
nouv = new t_ListeEntier;
// attribution de la valeur
// (*nouv).valeur = x ; juste mais pas facile
nouv->valeur = x; // notation équivalente plus simple
// élément suivant vaut null
nouv->suivant = NULL;
// renvoie d’un pointeur sur l’élément
return nouv;
}
⇤ Parcours de liste
Pour parcourir la liste, nous allons aller d’élément en élément en suivant l’élément pointé
par le champ suivant de chaque t listeEntier. En C++, nous allons utiliser une variable
locale de type t listeEntier * qui va permettre de pointer alternativement sur chaque
t listeEntier constituant la liste sans modifier le pointeur sur la tête de liste. fonction
afficherListe
spécification :
fonction : Ø afficherListe(l)
paramètres : t listeEntier l
résultats : Ø
algorithme
variables locales
t_listeEntier élément // élément courant qui va permettre de parcourir
la liste élément par élément
début
| élément l
| // boucle tant le dernier élément de la liste n’est pas atteint
| tant que élément existe faire
| | écrire (élément.valeur)
| | élément élément.suivant
| fin tant que
fin
/**
* Parcourt la liste et affiche les elements un a un.
*/
void afficheListe(t_ListeEntiers *l) {
// Creation d’une variable locale t pointeur sur un t_ListeEntiers pour parcourir
// la liste pour ne pas modifier le pointeur l sur la t^
ete de la liste.
t_ListeEntier *t = l;
cout << "[ ";
while (t) {
cout << t->valeur << " " ;
t = t->suivant;
}
cout << "]" << endl;
}
fonction supprimeElement
spécification :
fonction : nouvL supprimeElement(l, val)
paramètres : t listeEntier l
entier val //valeur de l’élément à supprimer
résultats : t listeEntier nouvL
algorithme
variables locales
t_listeEntier element // élément courant qui va permettre de parcourir
la liste élément par élément
t_listeEntier precedent
booléen trouvé
début
| si ( l non vide ) alors
| | trouvé faux
| | nouvL l
| | element l
| | // cas particulier : le premier élément doit ^
etre supprimé
| | si ( element.valeur = val ) alors
| | | nouvL nouvL.suivant
| | sinon
| | | precedent element
| | | element element.suivant
| | | // boucle tant le dernier élément de la liste n’est pas atteint
| | | tant que element existe et non trouvé faire
| | | | si ( element.valeur = val ) alors
| | | | | precedent.suivant element.suivant
| | | | | trouvé vrai
| | | | sinon
| | | | | precedent element
| | | | | element element.suivant
| | | | fin si
| | | fin tant que
| | fin si
| sinon
| | nouvL rien
| fin si
| renvoyer nouvL
fin
struct t_ListeEntier {
// type de la donnée : un entier
int valeur;
#include <stdlib.h>
#include <iostream>
using namespace std;
#include "listeEntier.h"
int main() {
t_ListeEntier * l1 = creeElement(3);
t_ListeEntier * l2 = creeElement(5);
t_ListeEntier * l3 = creeElement(7);
l1->suivant = l2;
l2->suivant = l3;
afficheListe(l1);
l1 = supprimeElementListe(l1, 5);
afficheListe(l1);
return 0;
}
L’exécution de ce programme donne l’affichage suivant :
[ 3 5 7 ]
[ 3 7 ]
1.2.1 Problème
La partie problème a un but documentaire, elle doit permettre à une personne qui prend
connaissance du dossier de savoir à quoi sert l’algorithme. Cette partie rappelle donc l’énoncé
du problème à traiter (le but du programme), elle précise les hypothèses du problème et
définit les résultats attendus.
1.2.2 Spécification
La partie spécification comporte le nom par lequel la fonction dont on fait le dossier
sera appelée ainsi que la liste des paramètres et des résultats de cette fonction, avec leurs
types et leurs rôles. C’est la partie « visible » de la fonction pour les autres fonctions. Elle
contient l’ensemble des informations nécessaires pour utiliser cette fonction.
S’il s’agit de la fonction principale, la partie spécification comporte la description des
types de données particuliers utilisés (matrice, enregistrement, . . . ) dans l’ensemble des fonc-
tions nécessaires pour résoudre le problème principal. Si aucun type de données particulier
n’est nécessaire, la partie spécification de la fonction principale est inutile.
1.2.3 Algorithme
Cette partie commence par la description des objets utilisés localement dans la fonction
(principales variables locales) : types, noms et commentaires sur leur rôle.
Puis l’algorithme détaille l’ensemble des instructions simples ou composées à exécuter
sur les objets précédemment décrits.
A des fins de lisibilité, l’ensemble de ces instructions, écrites entre les mots début et
fin devra être écrit sur une seule page de format A4. Si cela est impossible, certaines
instructions élémentaires seront regroupées sous la forme de fonctions clairement nommées,
accompagnées d’un dossier algorithmique.
début
| // initialisations
| s 0
| n lire ()
| tant que n6=999 faire
| | // ajouter n à s
| | s s + n
| | // lire l’élément suivant de la file
| | n lire ()
| fin tant que
| écrire (s)
| renvoyer s
fin
algorithme
début
| si ( u v ) alors
| | w u
| sinon
| | w v
| fin si
| renvoyer w
fin
Les seules instructions à utiliser sont celles données dans la partie langage (C/C++). Ce
sont des traductions des structures algorithmiques vues dans la partie algorithmique.
1.3.3 Commentaires
Les commentaires sont nécessaires pour :
– préciser le rôle des paramètres, résultats et variables,
– expliquer les idées sous-jacentes ou les points délicats des algorithmes,
On doit retrouver les commentaires utilisés dans les algorithmes. Par contre, il faut éliminer
les commentaires qui ne feraient que paraphraser le code et donc l’encombrer.
1.4 Résultats
Cette partie est composée de listages de di↵érents tests et jeux d’essai, ainsi que les
analyses et commentaires sur les résultats obtenus.
– pour un traitement de file, tester quelques exemples, notamment le cas éventuel où la
file est vide,
– pour un choix donné à l’utilisateur, donner un exemple où l’utilisateur donne une
réponse non proposée,
– pour le calcul d’une limite d’une suite numérique, donner un exemple où il y a conver-
gence, un exemple où la convergence n’est pas obtenue.
Exemple de rapport
2.1.1 Algorithmes
fonction principale
problème : Etant donnée une liste d’articles avec le nom, la référence et le prix unitaire,
l’afficher triée en ordre croissant de références et donner les valeurs minimum et maximum
des prix.
types
t_Article : Enregistrement
entier : reference,
chaine : libelle,
réel : prix
Fin_enregistrement
t_VectArticles : vecteur de t_Article
algorithme
début
| // obtenir les données
| {nA,listeArticles} lire ()
| // traiter les données
| listeArticles trierListeArticles(nA,listeArticles)
| {prixMin,prixMax} prixMinMax(nA,listeArticles)
| // donner les résultats
| écrire (nA, listeTriee, prixMin, prixMax)
fin
fonction trierListeArticles
problème : Ranger dans un nouveau vecteur les éléments d’un vecteur d’articles, en
les triant suivant l’ordre croissant du champ reference.
spécification :
fonction : listeTriee trierListeArticles(nbArt,listeArticles)
paramètres : entier nbArt
t VectArticles listeArticles
résultats : t VectArticles listeTriee
algorithme
début
| // initialisation
| listeTriee listeArticles
| // tri des articles
| pour i 1 à NbArt-1 faire
| | // recherche de l’indice de l’article de plus petite reference
| | min i
| | pour j i+1 à nbArt faire
| | | si ( listeTriee[j].reference < listeTriee[min].reference )
alors
| | | | min j
| | | fin si
| | fin pour
| | // permutation éventuelle des articles d’indices i et min
| | si ( min 6= i ) alors
| | | unArticle listeTriee[i]
| | | listeTriee[i] listeTriee[min]
| | | listeTriee[min] unArticle
| | fin si
| fin pour
| // renvoi des résultats
| renvoyer listeTriee
fin
fonction prixMinMax
problème : Chercher les valeurs minimum et maximum des prix des articles
spécification :
fonction : {prixMin,prixMax} prixMinMax(nbArt,listeArticles)
paramètres : entier nbArt
t VectArticles listeArticles
résultats : réels prixMin, prixMax
algorithme
début
| // recherche du min et du max
| iMin 1 ; iMax 1
| prixMin listeArticles[1].prix ; prixMax prixMin
| pour i 2 à nbArt faire
| | si ( listeArticles[i].prix < prixMin ) alors
| | | iMin i ; prixMin listeArticles[i].prix
| | fin si
| | si ( listeArticles[i].prix > prixMax ) alors
| | | iMax i ; prixMax listeArticles[i].prix
| | fin si
| fin pour
| // renvoi des résultats
| renvoyer prixMin, prixMax
fin
/*==========================================================================
Equipe Pedagogique d’Algorithmique et Programmation
Traitement d’une liste d’articles
-----------------------------------------------------------------------------
Fichier en-tete contenant la description des types utilises
Nom du fichier : TypesArticles.h
===========================================================================*/
# include <string>
/*==============================================================================
Equipe Pedagogique d’Algorithmique et Programmation
Traitement d’une liste d’articles
---------------------------------------------------------------------------------
Declaration des fonctions de manipulation des t_Articles
Nom du fichier : FonctionsArticles.h
================================================================================*/
/*==============================================================================
Equipe Pedagogique d’Algorithmique et Programmation
Traitement d’une liste d’articles
---------------------------------------------------------------------------------
Fonction principale :
Etant donnee une liste d’articles avec le nom, la reference et le prix unitaire,
les redonner tries en ordre croissant de references et donner les valeurs
minimum et maximum des prix. Utilisation d’une fonction de tri et d’une fonction
donnant les valeurs cherchees.
int main()
{
int nbArt;
t_VectArticles listeArticles;
float prixMin, prixMax ;
int i; // indice necessaire pour la lecture des articles
string nomFichier ;
fstream F;
/* obtenir les donnees : ici elles sont dans un fichier dont le nom
est demande a l’utilisateur */
cout<<"Nom du fichier ? "; cin >>nomFichier;
F.open(nomFichier.c_str(), ios::in);
//lire le nombre d’articles dans le fichier
F>>nbArt;
/*==============================================================================
Equipe Pedagogique d’Algorithmique et Programmation
Traitement d’une liste d’articles
================================================================================*/
/* Directives d’inclusion de fichiers */
# include <string>
using namespace std;
# include "TypesArticles.h" /* definition des types utilises */
# include "FonctionsArticles.h" /* declaration de la fonction trierListeArticles */
/*
Fonction TrierArticles :
Etant donnee une liste d’articles avec le nom, la reference et le prix unitaire,
trier cette liste en ordre croissant de references. Le tri utilise est un tri
par extraction du minimum.
/*
Fonction PrixMinMax :
Etant donnee une liste d’articles avec le nom, la reference et le prix unitaire,
trouver les valeurs minimum et maximum des prix. C’est un traitement de file.
Le nombre d’articles etant connu, utilisation d’une structure iterative "pour".
Remarques :
– L’ordre des compilations est indi↵érent,
– La liste des noms suivant la commande d’édition de liens doit commencer, par le nom
du fichier contenant la fonction principale (compilé).
# Fichier Makefile du programme de traitement d’Articles
# Les commentaires sont notes par un # en debut de ligne
# all : executer toutes les commandes ci-apres necessaires pour obtenir le fichier
# executable TtArticles.out
all : TtArticles.out
#----------------------------------
# Directives de compilation
#----------------------------------
# Donner la liste des fichiers dont depend la construction du fichier TtArticles.o :
# ce sont tous les fichiers inclus (hors ceux de la bibliotheque) et TtArticles.cxx
# La compilation ne sera faite que si l’un au moins de ces fichiers a ete modifie
# !! tabulation avant les commandes et / en fin de ligne si une commande
# prend plusieurs lignes
TtArticles.o : TtArticles.cxx TypesArticles.h FonctionsArticles.h
g++ -c TtArticles.cxx
#--------------------------------------
# Directives d’edition des liens
#--------------------------------------
# Liste des fichiers dont depend la construction du fichier Exemple.out
# tous les .o obtenus par compilation des differentes unites du programme
# et commande d’edition de liens
TtArticles.out : TtArticles.o FonctionsArticles.o
g++ -o TtArticles.out TtArticles.o FonctionsArticles.o
ANNEXE
S TRUCTURES ALGORITHMIQUES
Structures de choix
Instructions
Choix alternatif
Affectation
si nb1<nb2 alors if (nb1<nb2) {
Sx sin(A + 3 ⇤ x) Sx=sin(A+3*x) ;
écrire (nb2) cout<<nb2<<endl ;
matReels(nb1, nb2) 1 matReels[nb1][nb2]=1 ;
sinon } else {
artCour.reference 123 artCour.reference=123 ;
écrire (nb1) cout<<nb1<<endl ;
Lecture/Ecriture
fin si }
#include <iostream>
using namespace std ;
Choix sélectif
nb1 lire ( ) cin>>nb1 ;
artCour lire ( ) cin>>artCour.reference choix selon : switch (var) {
>>artCour.libelle var = 1 : case 1 :
>>artCour.prix ; écrire (“positif”) cout<<“positif” ;
écrire (nb1, nb2) cout<<“nb1 :”<<nb1 break ;
<<“nb2 :”<<nb2 ; var = -1 : case -1 :
écrire (vect) for(i=0 ; i<MAX ; i++){ écrire (“négatif”) cout<<“négatif” ;
cout<<vect[i]<<endl ; break ;
} autre : default :
écrire (“nul”) cout<<“nul” ;
fin choix selon }
Commentaires
/* commentaire C
Structures répétitives
/ commentaire / sur plusieurs lignes*/
// commentaire C++
Tant que . . .Faire
Structure générale nb1 lire ( ) cin>>nb1 ;
tant que nb1>0 faire while (nb1>0) {
problème . . . /* Explications somme somme+nb1 somme=somme+nb1 ;
sur le programme */ nb1 lire ( ) cin>>nb1 ;
spécification . . . /* inclusions */ fin tant que }
#include <...>
types . . . typedef ...
constantes . . . const ... Pour . . .Faire
algorithme int main() { pour i 0 à MAX faire for (i=0 ;i<=MAX ;i=i+1){
somme somme+vect(i) somme=somme+vect[i] ;
début //début du programme
fin pour }
corps de l’algorithme //corps du programme
fin } //fin du programme