Faculté Informatique & Communications Cours d'informatique |
Strings | Tableaux de taille fixe | Tableaux dynamiques | typedef | Structures | Pointeurs | Énumérations |
---|
#include<string>
La classe string est prévue pour stocker des chaînes de caractères. Elle s'utilise le plus souvent comme un type standard. Les valeurs littérales des chaînes de caractères sont entourées par des guillemets.
Exemples :
string c; // déclaration de la variable c de type string c = "Ceci est une chaîne de caractères"; // affectation string d("Ceci en est une deuxième"); // déclaration+initialisation string e; cin >> e; // l'utilisateur entre un mot, stocké dans e; cout << c << endl << d << endl << e << endl; // Affichage des chaînes
La classe string possède des fonctions spécifiques (ou "méthodes") : size(), substr, find, rfind, ...
Voici quelques exemples :
#include <string> #include <iostream> void main() { string machaine1 = "Bonjour"; string machaine2 = " à tous"; string tachaine; tachaine = machaine1[2]; // affecte le caractère d'index 2 de machaine1 // à tachaine : après, tachaine vaut "n" tachaine = machaine1 + machaine2; // affecte "Bonjour à tous" à tachaine cout << machaine1.size() // affiche la longueur de machaine1 : 7 << endl; cout << machaine2.substr(3,4) // affiche la sous-chaîne qui // commence au caractère d'indice 3 de machaine2 // (cad "t") et de longueur 4 // -> affiche "tous" << endl; cout << tachaine.find( "ou" ) // affiche la position de la première chaîne "ou" // -> affiche 4 << endl; cout << tachaine.rfind( "ou" ) // affiche la position de la première chaîne "ou" // à partir de la droite (right -> *r*find) // -> affiche 11; << endl; }
Le type des objets contenus dans le tableau est appelé type de base du tableau, et il peut être quelconque ; il peut notamment s'agir d'un autre tableau.
La déclaration d'un tableau se fait au moyen de la syntaxe suivante :
type identificateur[taille];où :
Il est possible d'initialiser une variable ou constante tableau lors de sa déclaration en spécifiant une partie ou tous les littéraux constituant les valeurs initiales.
Dans ce cas, la spécification de la taille du tableau est optionnelle; si elle n'est pas présente, la taille sera déterminée par le nombre de littéraux spécifiés.
type identificateur[N] = { val1, ..., valN };
L'accès individuel aux éléments se fait en postfixant l'identificateur de tableau par un index spécifié entre crochets (parenthèses carrées) :
identificateur[index];
Il n'y a pas de test de débordement !!
C'est à dire qu'une erreur comme int tab[4]; int i(3); ... i=i+2; ... tab[i] ... // tab[5] !!n'est pas détectée, ni par le compilateur ni lors de l'execution. |
#include <iostream> /* * But : réaliser l'affichage à l'écran des paramètres * d'un objet géométrique. * * Entrée: titre = nom de la figure * params = paramètres de la figure : tableau d'entiers * nombre = nombre de 'paramètres' dans le tableau */ void afficherParametres(const string& titre, const int params[], const unsigned int nombre) { cout << "Paramètres de la figure '" << titre << "' :" << endl; for(unsigned int p(0); p < nombre; p++) cout << '[' << p << "] = " << params[p] << endl; cout << endl; // saut de ligne supplémentaire } main() { const int taille_rectangle(2); int rectangle1[taille_rectangle]; // déclaration de variable int parallelepipede[3] = {2, 3, 1}; // décl.-init. de variable const int rectangle2[taille_rectangle] = {5, 3}; // déclaration de constante rectangle1[0] = 1; rectangle1[1] = 3; afficherParametres("rectangle numéro 1", rectangle1, taille_rectangle); afficherParametres("rectangle numéro 2", rectangle2, taille_rectangle); // NIVEAU AVANCÉ : spécification de la taille par le biais de sizeof. // Au niveau "normal", préférez utiliser des constantes. afficherParametres("parallelepipede 1", parallelepipede, sizeof(parallelepipede)/sizeof(parallelepipede[0])); }
Paramètres de la figure 'rectangle numéro 1' : [0] = 1 [1] = 3 Paramètres de la figure 'rectangle numéro 2' : [0] = 5 [1] = 3 Paramètres de la figure 'parallelepipede 1' : [0] = 2 [1] = 3 [2] = 1
Pour utiliser le type vector, permettant de mettre
en oeuvre les tableaux dynamiques, les programmes doivent importer la librairie
de définition de ce type, au moyen de la directive : #include <vector> |
vector < type > identificateur;
Si l'on connaît à priori la dimension (dim_vec) du tableau dynamique, celui-ci peut être déclaré ainsi :
vector < type > identificateur(dim_vect);
Si l'on connaît la taille (initiale) du tableau dynamique lors de sa déclaration, on peut alors initialiser ses éléments, et leur affecter à tous la même valeur valeur, en utilisant la syntaxe suivante :
vector < type > identificateur(dim_vect, valeur);
la valeur d'initialisation valeur doit évidemment être du type type. |
La syntaxe pour accéder à la valeur d'un élément d'un tableau dynamique est :
identificateur[indice];
identificateur[indice] = valeur;mais on peut aussi utiliser la fonction spécifique pushd expliquée ci-dessous
int size() | renvoie la taille du tableau dynamique |
bool empty() | renvoie true si le tableau dynamique est vide (de taille nulle), false sinon |
void clear() | vide le tableau dynamique de ses éléments |
void pop_back() | supprime le dernier élément du tableau dynamique |
void push_back(type-base) | ajoute l'élément <valeur> à la fin du tableau dynamique |
base-type& front() | renvoie une référence au premier élément du tableau |
base-type& back() | renvoie une référence au dernier élément du tableau |
Notez que les méthodes clear, pop_back et push_back modifient la taille (i.e. le nombre d'éléments) du tableau dynamique.
vector<int> tab(3, 0); // déclaration et initialisation à 0 d'un // tableau dynamique d'entiers de dimension 3 tab[2] = -45; // affectation de la valeur -45 à l'élément // de position 2 du tableau dynamique tab cout << tab[1] << endl; // affichage de l'élément // de position 1 du tableau tab -> 0 cout << tab.size() << endl; // affichage de la taille // du tableau tab -> 3 tab.push_back(20); // ajout à la fin du tableau d'un élément valant 20 cout << tab.size() << " "; // affichage taille -> 4 tab.pop_back(); // suppression du dernier élément cout << tab.back() << endl;// affichage du dernier élément -> -45 tab.clear(); // vidage du tableau cout << tab.size() << endl;// affichage taille tableau -> 0
Le spécificateur (mot clef) «typedef
» permet
de définir un synonyme (alias) pour un type. Il y a deux
raisons principales militant en faveur d'une utilisation
intensive de cette clause: tout d'abord, typedef permet
de faciliter l'écriture des types complexes, en décomposant les étapes
de la définition, et en rendant réutilisable ces définitions (pas
besoin de réécrire des déclarations complexes). La seconde raison (non
sans rapport avec la première) est le regroupement des définitions,
lié à une plus forte explicitation d'informations (voir ci-après).
Syntaxe : | typedef type alias; |
Après la déclaration d'un type synonyme, l'alias peut être utilisé exactement comme un type prédéfini du langage.
En fait, l'utilisation de ce spécificateur est motivée par les mêmes raisons que celles qui valident l'utilisation de fonctions pour mettre en oeuvre du code réutilisable. Il permet d'ajouter de l'information conceptuelle (i.e. expliciter des informations implicites), en établissant une distinction lors d'utilisation multiple d'un même type de base.
typedef vector< vector <double> > Matrice; typedef int distance; typedef distance surface; typedef distance volume;
L'information explicitée dans ce dernier exemple est clairement visible.
L'avantage d'une telle spécification, par rapport à la donnée
systématique des types de base (int
dans ce cas), est
évidente : si, pour des raisons quelconques, il devient
nécessaire de
modifier la représentation utilisée pour l'une des grandeurs (par exemple,
les distances doivent être représentées par des réels), il suffira
de simplement modifier la définition de l'alias distance
;
sans définition unique, au moyen de typedef
, de la notion
de distance, il faudrait parcourir tous le (les) programme(s), en traquant
tous les entiers représentant potentiellement une distance.
Une seconde raison (et pas des moins importantes) pour une telle
spécification
est d'augmenter le degré de lisibilité des
programmes. Même si
le compilateur accepte, par exemple, l'affectation d'une variable de type
volume
par une expression de type distance
, puisqu'il
s'agit en fait du
même type, les risques de telles confusions pour le programmeur
humain sont malgrés tout moindres.
Les structures sont simplement des regroupements de plusieurs variables appelées champs de la structure. Les champs peuvent être de tous types (élémentaires ou complexes) et pas forcément du tous du même (type).
Les structures présentent donc l'intérêt de pourvoir regrouper des entités pour leur manipulation unique.
La syntaxe de déclaration d'une structure est :
struct nom { type1 champ1; ... typeN champN; };
où nom est le nom que vous souhaitez donner à la
structure, type1 est le type du premier champ et
champ1 son nom, etc...
L'ordre des champs n'a pas d'importance.
Exemples :
struct Complexe { double x; double y; }; struct Personne { string nom; unsigned short int age; char sexe; double taille; unsigned short int poids; };
Une fois définie, une struct s'utilise comme n'importe quel autre type.
Exemples :
Complexe z; Personne untel;
On peut également initialiser une structure lors de sa déclaration. Cela se fait avec la syntaxe suivante :
nom variable = { val1, ... valN };
où nom est le nom de la structure, variable le nom de la variable déclarée et val1 une valeur du type correspondant au premier champ défini dans la structure (type1 dans la définition précédente).
Exemples :
Complexe z = { 0.0, 1.0 }; Personne untel = { "Pierre", 19, 'M', 1.82, 75 };
Pour accéder à un champ particulier d'une structure, on utilise la syntaxe :
variable.champ
où variable le nom de la variable (de type "structure") et champ le champ considéré.
Exemples (continuation des précédents) :
z.x = -3; ... z.y * z.y + z.x * z.x ... untel.age++;
Un pointeur est une variable contenant l'adresse d'un autre objet informatique. C'est donc une indirection.
Il n'y a rien de particulier par rapport aux pointeurs si ce n'est qu'ils représentent un niveau d'abstration supplémentaire. C'est peut être là que réside la difficulté de le compréhension.
La déclaration d'un pointeur se fait en ajoutant une étoile (*) après le type que l'on veut pointer.
Par exemple, pour déclarer une variable (nommée ptr) pointant sur un entier on écrira :
int* ptr;
Le type pointé peut être n'importe quel type valide : élémentaire ou complexe (ce peut même être un pointeur !).
Pour accéder au contenu pointé par un pointeur, il faut écrire :
*ptr
Pour faire pointer un pointeur sur une autre variable on utilisera l'opérateur d'adresse & (qui renvoit l'adresse de la variable à laquelle il s'applique). Par exemple :
int i(4); int j(5); int* ptr(&i); // ptr pointe sur i cout << *ptr << endl; // affiche 4 ptr = &j; // ptr pointe maintenant sur j j++; // j vaut maintenant 6 cout << *ptr << endl; // affiche 6 *ptr = *prt + 2; // augmente la valeur pointée (donc celle de j) cout << j << endl; // affiche 8
Les pointeurs peuvent aussi pointer sur des fonctions. Exemple :
int f(int,double); // prototype d'une fonction (prennant un int et // un double et retournant un entier) int (*ptr)(int,double); // pointeur sur une telle fonction ptr = f; // ici ptr pointe sur f, c'est la MÊME chose que ptr = &f; // avec les fonction on n'a pas besoin du &, mais on peut // le mettre si on préfère.
On peut également allouer directement la mémoire que l'on veut
pointer, sans que ce soit un objet déjà définit.
Dans ce cas, il faut également penser à rendre
la mémoire allouée lorsqu'on en a plus besoin.
Pour allouer de la mémoire on utiliser new et pour la rendre delete.
Exemple :
int *ptr(0); // ptr ne pointe sur rien pour l'instant ptr = new int; // ptr pointe maintenant sur une zone mémoire // prète à recevoir un entier (int) // (mais pas encore initialisée) *ptr = 5; // remplit la zone mémoire pointée par prt avec la valeur 5 // plus haut on aurait aussi pu écrire : // ptr = new int(5); // pour faire une initialisation en même temps que l'allocation mémoire ... delete ptr; // on a plus besoin de prt : libère la zone mémoire // qu'on lui avait alloué.
Une énumération est un type de donnée qui contient une liste de mot-clefs définis par l'utilisateur et les associe à des entiers. Par exemple,
enum couleur { rouge, vert, bleu };
associe à «rouge» la valeur 0, à «vert» la valeur 1, et à «bleu» la valeur 2. On utilise une énumération de la façon suivante :
enum couleur { rouge, vert, bleu }; // ceci est bien une déclaration de type void foobar(couleur maCouleur) // là, on prend en argument un objet de type "couleur". { switch (maCouleur) { case rouge: // faire quelque chose pour rouge break; case vert: // faire quelque chose pour vert break; case bleu: // faire quelque chose pour bleu break; } }
Il est par ailleurs possible d'affecter soi-même une valeur à un élément de la liste d'options :
enum couleur { rouge, vert=4, bleu };
associe à «rouge» la valeur 0, à «vert» la valeur 4, et à «bleu» la valeur 5.