|
Faculté Informatique & Communications Cours d'informatique |
![]() |
| Strings | Tableaux de taille fixe | Vectors | typedef | Structures | Pointeurs |
|---|
#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 :
La classe string posséde des fonctions spécifiques (ou "méthodes") : size(), substr, find, rfind, ...
Voici quelques exemples :
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. |
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> |
Si l'on connaît à priori la dimension du vecteur (dim_vec), celui-ci peut être déclaré ainsi :
Si l'on connaît la taille du vecteur 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 :
| la valeur d'initialisation valeur doit évidemment être du type type. |
La syntaxe pour accéder à la valeur d'un élément d'un vecteur est :
| int size() | renvoie la taille du vecteur |
| bool empty() | renvoie true si le vecteur est vide (de taille nulle), false sinon |
| void clear() | vide le vecteur de ses éléments |
| void pop_back() | supprime le dernier élément du vecteur |
| void push_back(type-base) | ajoute l'élément <valeur> à la fin du vecteur |
| 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 vecteur.
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> 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.champoù 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é.