6.14 - Pointeurs vers des pointeurs et des tableaux multidimensionnels dynamiques C++ | Exam-Lib
  1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
Dismiss Notice
Welcome to our Education website, plz like our page facebook to support us. Thank You and wish you good navigation

6.14 - Pointeurs vers des pointeurs et des tableaux multidimensionnels dynamiques C++

abdelouafiDec 2, 2018

    1. abdelouafi

      abdelouafi Administrator Staff Member

      Messages:
      893
      Likes Received:
      14
      Trophy Points:
      18
      Joined
      Sep 13, 2016
      Cette leçon est facultative pour les lecteurs avancés souhaitant en savoir plus sur C ++. Aucune leçon future ne s'appuie sur cette leçon.

      Un pointeur sur un pointeur correspond exactement à ce que vous attendez: un pointeur contenant l’adresse d’un autre pointeur.

      Pointeurs à pointeurs

      Un pointeur normal sur un int est déclaré à l'aide d'un seul astérisque:
      Code:
      int *ptr; // pointer to an int, one asterisk
      
      Un pointeur sur un pointeur sur un int est déclaré à l'aide de deux astérisques
      Code:
      int **ptrptr; // pointer to a pointer to an int, two asterisks
      
      Un pointeur sur un pointeur fonctionne comme un pointeur normal: vous pouvez le déréférencer pour récupérer la valeur pointée. Et comme cette valeur est elle-même un pointeur, vous pouvez la déréférencer de nouveau pour atteindre la valeur sous-jacente. Ces déréférences peuvent être effectuées consécutivement:
      Code:
      int value = 5;
      int *ptr = &value;
      std::cout << *ptr; // dereference pointer to int to get int value
      int **ptrptr = &ptr;
      std::cout << **ptrptr; // first dereference to get pointer to int, second dereference to get int value
      Cela affiche :
      5
      5

      Notez que vous ne pouvez pas définir un pointeur sur un pointeur directement sur une valeur:
      Code:
      int value = 5;
      int **ptrptr = &&value; // not valid
      En effet, l'adresse de l'opérateur (operator &) requiert une lvalue, mais & value est une rvalue.

      Cependant, un pointeur sur un pointeur peut être défini sur null:
      Code:
      int **ptrptr = nullptr; // use 0 instead prior to C++11
      
      Tableaux de pointeurs

      Les pointeurs sur les pointeurs ont quelques utilisations. L’utilisation la plus courante consiste à allouer de manière dynamique un tableau de pointeurs:
      Code:
      int **array = new int*[10]; // allocate an array of 10 int pointers
      
      Cela fonctionne comme un tableau standard alloué dynamiquement, à la différence que les éléments du tableau sont de type «pointeur sur entier» et non de type entier.

      Tableaux bidimensionnels alloués dynamiquement

      Une autre utilisation courante des pointeurs vers des pointeurs est de faciliter les tableaux multidimensionnels alloués de façon dynamique (voir 6.5 - Tableaux multidimensionnels pour un aperçu des tableaux multidimensionnels).

      Contrairement à un tableau fixe bidimensionnel, qui peut facilement être déclaré comme ceci:
      Code:
      int array[10][5];
      
      Allouer dynamiquement un tableau à deux dimensions est un peu plus difficile. Vous pourriez être tenté d'essayer quelque chose comme ceci:
      Code:
      int **array = new int[10][5]; // won’t work!
      
      Mais ça ne marchera pas.

      Il y a deux solutions possibles ici. Si la dimension de tableau la plus à droite est une constante de compilation, vous pouvez procéder comme suit:
      Code:
      int (*array)[5] = new int[10][5];
      
      Les parenthèses sont nécessaires ici pour assurer la priorité. En C ++ 11 ou plus récent, c'est un bon endroit pour utiliser la déduction de type automatique:
      Code:
      auto array = new int[10][5]; // so much simpler!
      
      Malheureusement, cette solution relativement simple ne fonctionne pas si la dimension de tableau la plus à droite n’est pas une constante de compilation. Dans ce cas, nous devons être un peu plus compliqués. Premièrement, nous allouons un tableau de pointeurs (comme ci-dessus). Ensuite, nous parcourons le tableau de pointeurs et allouons un tableau dynamique pour chaque élément du tableau. Notre tableau dynamique à deux dimensions est un tableau dynamique à une dimension de tableaux dynamiques à une dimension!
      Code:
      int **array = new int*[10]; // allocate an array of 10 int pointers — these are our rows
      for (int count = 0; count < 10; ++count)
          array[count] = new int[5]; // these are our columns
      Nous pouvons ensuite accéder à notre tableau comme d'habitude:
      Code:
      array[9][4] = 3; // This is the same as (array[9])[4] = 3;
      
      Avec cette méthode, étant donné que chaque colonne de tableau est allouée dynamiquement indépendamment, il est possible de créer des tableaux bidimensionnels alloués dynamiquement qui ne sont pas rectangulaires. Par exemple, nous pouvons créer un tableau en forme de triangle:
      Code:
      int **array = new int*[10]; // allocate an array of 10 int pointers — these are our rows
      for (int count = 0; count < 10; ++count)
          array[count] = new int[count+1]; // these are our columns
      Dans l'exemple ci-dessus, notez que tableau [0] est un tableau de longueur 1, tableau [1] est un tableau de longueur 2, etc.

      La désallocation d'un tableau à deux dimensions alloué dynamiquement à l'aide de cette méthode nécessite également une boucle:
      Code:
      for (int count = 0; count < 10; ++count)
          delete[] array[count];
      delete[] array; // this needs to be done last
      Notez que nous supprimons le tableau dans l'ordre inverse de notre création (éléments en premier, puis le tableau lui-même). Si nous supprimons un tableau avant les éléments du tableau, nous devrons alors accéder à la mémoire désallouée pour supprimer les éléments du tableau. Et cela se traduirait par un comportement indéfini.

      Comme l’allocation et la désallocation de tableaux à deux dimensions sont complexes et faciles à gâcher, il est souvent plus facile d’aplatir un tableau à deux dimensions (de taille x sur y) en un tableau à une dimension de taille x * y:
      Code:
      // Instead of this:
      int **array = new int*[10]; // allocate an array of 10 int pointers — these are our rows
      for (int count = 0; count < 10; ++count)
          array[count] = new int[5]; // these are our columns
      // Do this
      int *array = new int[50]; // a 10x5 array flattened into a single array
      Des calculs simples peuvent ensuite être utilisés pour convertir un index de ligne et de colonne pour un tableau rectangulaire à deux dimensions en un seul index pour un tableau à une dimension:
      Code:
      int getSingleIndex(int row, int col, int numberOfColumnsInArray)
      {
           return (row * numberOfColumnsInArray) + col;
      }
      // set array[9,4] to 3 using our flattened array
      array[getSingleIndex(9, 4, 5)] = 3;
      Passer un pointeur par adresse

      Tout comme nous pouvons utiliser un paramètre de pointeur pour changer la valeur réelle de l'argument sous-jacent transmis, nous pouvons passer un pointeur à un pointeur sur une fonction et utiliser ce pointeur pour changer la valeur du pointeur sur lequel il pointe (encore confus?) .

      Cependant, si nous souhaitons qu'une fonction puisse modifier le pointeur d'un argument de pointeur, il est généralement préférable d'utiliser plutôt une référence à un pointeur. Nous n’en parlerons donc plus ici.

      Nous parlerons davantage de passage par adresse et passage par référence dans le chapitre suivant.

      Pointeur sur un pointeur vers un pointeur sur…

      Il est également possible de déclarer un pointeur sur un pointeur vers un pointeur:
      Code:
      int ***ptrx3;
      
      Ceux-ci peuvent être utilisés pour allouer dynamiquement un tableau en trois dimensions. Cependant, cela nécessiterait une boucle à l'intérieur d'une boucle et est extrêmement compliqué à corriger.

      Vous pouvez même déclarer un pointeur sur un pointeur vers un pointeur sur un pointeur:
      Code:
      int ****ptrx4;
      
      Ou plus haut, si vous le souhaitez.

      Cependant, en réalité, ils ne sont pas très utiles car il est rare que vous ayez besoin de tant d’indirection.

      Conclusion

      Nous vous recommandons d’éviter d’utiliser des pointeurs sur des pointeurs à moins qu’aucune autre option ne soit proposée, car ils sont compliqués à utiliser et potentiellement dangereux. Il est assez facile de déréférencer un pointeur nul ou suspendu avec des pointeurs normaux - c’est encore plus facile avec un pointeur vers un pointeur, car vous devez faire un double déréférencement pour obtenir la valeur sous-jacente!
       
      Related Threads
      Loading...

      Merci de partager ce post sur facebook

Share This Page

Share