DESIGN STUDIO FORUM
Vous souhaitez réagir à ce message ? Créez un compte en quelques clics ou connectez-vous pour continuer.



 
PortailAccueilRechercherDernières imagesS'enregistrerConnexion
Le Deal du moment : -25%
PC Portable Gamer 16,1” HP Victus 16 – 16 ...
Voir le deal
749.99 €

 

 Chapitre4.Les pointeurs et références (3)

Aller en bas 
AuteurMessage
didouqen
Admin
didouqen


Masculin
Nombre de messages : 40
Age : 39
Localisation : Marrakech
Date d'inscription : 07/09/2006

Chapitre4.Les pointeurs et références (3) Empty
MessageSujet: Chapitre4.Les pointeurs et références (3)   Chapitre4.Les pointeurs et références (3) EmptyMar 25 Sep - 7:22

4.8. Arithmétique des pointeurs
Il est possible d'effectuer des opérations arithmétiques sur les pointeurs.
Les seules opérations valides sont les opérations externes (addition et soustraction des entiers) et la soustraction de pointeurs. Elles sont définies comme suit (la soustraction d'un entier est considérée comme l'addition d'un entier négatif) :
p + i = adresse contenue dans p + i*taille(élément pointé par p)
et :
p2 - p1 = (adresse contenue dans p2 - adresse contenue dans p1) /
taille(éléments pointés par p1 et p2)
Si p est un pointeur d'entier, p+1 est donc le pointeur sur l'entier qui suit immédiatement celui pointé par p. On retiendra surtout que l'entier qu'on additionne au pointeur est multiplié par la taille de l'élément pointé pour obtenir la nouvelle adresse.
Le type du résultat de la soustraction de deux pointeurs est très dépendant de la machine cible et du modèle mémoire du programme. En général, on ne pourra jamais supposer que la soustraction de deux pointeurs est un entier (que les chevronnés du C me pardonnent, mais c'est une erreur très grave). En effet, ce type peut être insuffisant pour stocker des adresses (une machine peut avoir des adresses sur 64 bits et des données sur 32 bits). Pour résoudre ce problème, le fichier d'en-tête stdlib.h contient la définition du type à utiliser pour la différence de deux pointeurs. Ce type est nommé ptrdiff_t.
Exemple 4-10. Arithmétique des pointeurs
int i, j;
ptrdiff_t delta = &i - &j; /* Correct */
int error = &i - &j; /* Peut marcher, mais par chance. */
Il est possible de connaître la taille d'un élément en caractères en utilisant l'opérateur sizeof. Il a la syntaxe d'une fonction :
sizeof(type|expression)
Il attend soit un type, soit une expression. La valeur retournée est soit la taille du type en caractères, soit celle du type de l'expression. Dans le cas des tableaux, il renvoie la taille totale du tableau. Si son argument est une expression, celle-ci n'est pas évaluée (donc si il contient un appel à une fonction, celle-ci n'est pas appelée). Par exemple :
sizeof(int)
renvoie la taille d'un entier en caractères, et :
sizeof(2+3)
renvoie la même taille, car 2+3 est de type entier. 2+3 n'est pas calculé.
Note : L'opérateur sizeof renvoie la taille des types en tenant compte de leur alignement. Cela signifie par exemple que même si un compilateur espace les éléments d'un tableau afin de les aligner sur des mots mémoire de la machine, la taille des éléments du tableau sera celle des objets de même type qui ne se trouvent pas dans ce tableau (ils devront donc être alignés eux aussi). On a donc toujours l'égalité suivante :
sizeof(tableau) = sizeof(élément) * nombre d'éléments
4.9. Utilisation des pointeurs avec les tableaux
Les tableaux sont étroitement liés aux pointeurs parce que, de manière interne, l'accès aux éléments des tableaux se fait par manipulation de leur adresse de base, de la taille des éléments et de leurs indices. En fait, l'adresse du n-ième élément d'un tableau est calculée avec la formule :
Adresse_n = Adresse_Base + n*taille(élément)
où taille(élément) représente la taille de chaque élément du tableau et Adresse_Base l'adresse de base du tableau. Cette adresse de base est l'adresse du début du tableau, c'est donc à la fois l'adresse du tableau et l'adresse de son premier élément.
Ce lien apparaît au niveau du langage dans les conversions implicites de tableaux en pointeurs, et dans le passage des tableaux en paramètre des fonctions.
4.9.1. Conversions des tableaux en pointeurs
Afin de pouvoir utiliser l'arithmétique des pointeurs pour manipuler les éléments des tableaux, le C++ effectue les conversions implicites suivantes lorsque nécessaire :
• tableau vers pointeur d'élément ;
• pointeur d'élément vers tableau.
Cela permet de considérer les expressions suivantes comme équivalentes :
identificateur[n]
et :
*(identificateur + n)
si identificateur est soit un identificateur de tableau, soit celui d'un pointeur.
Exemple 4-11. Accès aux éléments d'un tableau par pointeurs
int tableau[100];
int *pi=tableau;

tableau[3]=5; /* Le 4ème élément est initialisé à 5 */
*(tableau+2)=4; /* Le 3ème élément est initialisé à 4 */
pi[5]=1; /* Le 6ème élément est initialisé à 1 */
Note : Le langage C++ impose que l'adresse suivant le dernier élément d'un tableau doit toujours être valide. Cela ne signifie absolument pas que la zone mémoire référencée par cette adresse est valide, bien au contraire, mais plutôt que cette adresse est valide. Il est donc garantit que cette adresse ne sera pas le pointeur NULL par exemple, ni toute autre valeur spéciale qu'un pointeur ne peut pas stocker. Il sera donc possible de faire des calculs d'arithmétique des pointeurs avec cette adresse, même si elle ne devra jamais être déréférencée, sous peine de voir le programme planter.
On prendra garde à certaines subtilités. Les conversions implicites sont une facilité introduite par le compilateur, mais en réalité, les tableaux ne sont pas des pointeurs, ce sont des variables comme les autres, à ceci près : leur type est convertible en pointeur sur le type de leurs éléments. Il en résulte parfois quelques ambiguïtés lorsqu'on manipule les adresses des tableaux. En particulier, on a l'égalité suivante :
&tableau == tableau
en raison du fait que l'adresse du tableau est la même que celle de son premier élément. Il faut bien comprendre que dans cette expression, une conversion a lieu. Cette égalité n'est donc pas exacte en théorie. En effet, si c'était le cas, on pourrait écrire :
*&tableau == tableau
puisque les opérateurs * et & sont conjugués, d'où :
tableau == *&tableau = *(&tableau) == *(tableau) == t[0]
ce qui est faux (le type du premier élément n'est en général pas convertible en type pointeur.).
4.9.2. Paramètres de fonction de type tableau
La conséquence la plus importante de la conversion tableau vers pointeur se trouve dans le passage par variable des tableaux dans une fonction. Lors du passage d'un tableau en paramètre d'une fonction, la conversion implicite a lieu, les tableaux sont donc toujours passés par variable, jamais par valeur. Il est donc faux d'utiliser des pointeurs pour les passer en paramètre, car le paramètre aurait le type pointeur de tableau. On ne modifierait pas le tableau, mais bel et bien le pointeur du tableau. Le programme aurait donc de fortes chances de planter.
Par ailleurs, certaines caractéristiques des tableaux peuvent être utilisées pour les passer en paramètre dans les fonctions.
Il est autorisé de ne pas spécifier la taille de la dernière dimension des paramètres de type tableau dans les déclarations et les définitions de fonctions. En effet, la borne supérieure des tableaux n'a pas besoin d'être précisée pour manipuler leurs éléments (on peut malgré tout la donner si cela semble nécessaire).
Cependant, pour les dimensions deux et suivantes, les tailles des premières dimensions restent nécessaires. Si elles n'étaient pas données explicitement, le compilateur ne pourrait pas connaître le rapport des dimensions. Par exemple, la syntaxe :
int tableau[][];
utilisée pour référencer un tableau de 12 entiers ne permettrait pas de faire la différence entre les tableaux de deux lignes et de six colonnes et les tableaux de trois lignes et de quatre colonnes (et leurs transposés respectifs). Une référence telle que :
tableau[1][3]
ne représenterait rien. Selon le type de tableau, l'élément référencé serait le quatrième élément de la deuxième ligne (de six éléments), soit le dixième élément, ou bien le quatrième élément de la deuxième ligne (de quatre éléments), soit le huitième élément du tableau. En précisant tous les indices sauf un, il est possible de connaître la taille du tableau pour cet indice à partir de la taille globale du tableau, en la divisant par les tailles sur les autres dimensions (2 = 12/6 ou 3 = 12/4 par exemple).
Le programme d'exemple suivant illustre le passage des tableaux en paramètre :
Exemple 4-12. Passage de tableau en paramètre
int tab[10][20];

void test(int t[][20])
{
/* Utilisation de t[i][j] ... */
return;
}

int main(void)
{
test(tab); /* Passage du tableau en paramètre. */
return 0;
}
4.10. Les chaînes de caractères : pointeurs et tableaux à la fois !
On a vu dans le premier chapitre que les chaînes de caractères n'existaient pas en C/C++. Ce sont en réalité des tableaux de caractères dont le dernier caractère est le caractère nul.
Cela a plusieurs conséquences. La première, c'est que les chaînes de caractères sont aussi des pointeurs sur des caractères, ce qui se traduit dans la syntaxe de la déclaration d'une chaîne de caractères constante :
const char *identificateur = "chaîne";
identificateur est déclaré ici comme étant un pointeur de caractère, puis il est initialisé avec l'adresse de la chaîne de caractères constante "chaîne".
La deuxième est le fait qu'on ne peut pas faire, comme en Pascal, des affectations de chaînes de caractères, ni des comparaisons. Par exemple, si « nom1 » et « nom2 » sont des chaînes de caractères, l'opération :
nom1=nom2;
n'est pas l'affectation du contenu de nom2 à nom1. C'est une affectation de pointeur : le pointeur nom1 est égal au pointeur nom2 et pointent sur la même chaîne ! Une modification de la chaîne pointée par nom1 entraîne donc la modification de la chaîne pointée par nom2...
De même, le test nom1==nom2 est un test entre pointeurs, pas entre chaînes de caractères. Même si deux chaînes sont égales, le test sera faux si elles ne sont pas au même emplacement mémoire.
Il existe dans la bibliothèque C de nombreuses fonctions permettant de manipuler les chaînes de caractères. Par exemple, la copie d'une chaîne de caractères dans une autre se fera avec les fonctions strcpy et strncpy, la comparaison de deux chaînes de caractères pourra être réalisée à l'aide des fonctions strcmp et strncmp, et la détermination de la longueur d'une chaîne de caractères à l'aide de la fonction strlen. Je vous invite à consulter la documentation de votre environnement de développement ou la bibliographie pour découvrir toutes les fonctions de manipulation des chaînes de caractères. Nous en verrons un exemple d'utilisation dans la section suivante.
Revenir en haut Aller en bas
http://didouqen.ousama.free.fr
 
Chapitre4.Les pointeurs et références (3)
Revenir en haut 
Page 1 sur 1
 Sujets similaires
-

Permission de ce forum:Vous ne pouvez pas répondre aux sujets dans ce forum
DESIGN STUDIO FORUM :: programmation :: C,C++-
Sauter vers:  
Ne ratez plus aucun deal !
Abonnez-vous pour recevoir par notification une sélection des meilleurs deals chaque jour.
IgnorerAutoriser