454 votes

Pourquoi utiliser des pointeurs ?

Je sais que c'est une question vraiment basique, mais je viens de commencer à faire de la programmation de base en C++ après avoir codé quelques projets avec des langages de haut niveau.

En fait, j'ai trois questions :

  1. Pourquoi utiliser des pointeurs plutôt que des variables normales ?
  2. Quand et où dois-je utiliser les pointeurs ?
  3. Comment utiliser les pointeurs avec les tableaux ?

8 votes

Discuté précédemment à l'adresse suivante cette question J'espère que cela vous aidera !

0 votes

Une autre discussion sur les pointeurs ici .

4 votes

Pour une liste de livres, voir stackoverflow.com/questions/388242/ . Après Java, j'ai trouvé C++ accéléré très utile.

223voto

Tooony Points 1212
  • Pourquoi utiliser des pointeurs plutôt que des variables normales ?

La réponse courte est : Ne le faites pas. ;-) Les pointeurs doivent être utilisés lorsque vous ne pouvez pas utiliser autre chose. C'est soit parce qu'il n'y a pas de fonctionnalité appropriée, soit parce qu'il manque des types de données, soit par pur souci de performance. Plus d'informations ci-dessous...

  • Quand et où dois-je utiliser les pointeurs ?

La réponse courte ici est : Lorsque vous ne pouvez pas utiliser autre chose. En C, vous ne disposez d'aucun support pour les types de données complexes tels que les chaînes de caractères. Il n'y a pas non plus de moyen de passer une variable "par référence" à une fonction. C'est là que vous devez utiliser des pointeurs. De plus, vous pouvez les utiliser pour pointer vers n'importe quoi, des listes liées, des membres de structures, etc. Mais ne parlons pas de cela ici.

  • Comment utiliser les pointeurs avec les tableaux ?

Avec peu d'efforts et beaucoup de confusion ;-) Si nous parlons de types de données simples tels que int et char, il y a peu de différence entre un tableau et un pointeur. Ces déclarations sont toutes deux identiques

char* a = "Hello";
char a[] = "Hello";

Vous pouvez atteindre n'importe quel élément du tableau comme ceci

printf("Second char is: %c", a[1]);

Index 1 puisque le tableau commence par l'élément 0. :-)

Ou vous pouvez également faire ceci

printf("Second char is: %c", *(a+1));

L'opérateur pointeur (le *) est nécessaire puisque nous disons à printf que nous voulons imprimer un caractère. Sans le *, la représentation de l'adresse mémoire elle-même serait imprimée. Maintenant, nous utilisons le caractère lui-même à la place. Si nous avions utilisé %s au lieu de %c, nous aurions demandé à printf d'imprimer le contenu de l'adresse mémoire pointée par 'a' plus un (dans l'exemple ci-dessus), et nous n'aurions pas eu besoin de mettre le * devant :

printf("Second char is: %s", (a+1)); /* WRONG */

Mais cela n'aurait pas seulement imprimé le deuxième caractère, mais tous les caractères des adresses mémoire suivantes, jusqu'à un caractère nul ( \0 ) ont été trouvés. Et c'est là que les choses commencent à devenir dangereuses. Que se passe-t-il si vous essayez accidentellement d'imprimer une variable de type integer au lieu d'un pointeur char avec le formateur %s ?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

Cela imprimerait ce qui se trouve à l'adresse mémoire 120 et continuerait à imprimer jusqu'à ce qu'un caractère nul soit trouvé. L'exécution de cette instruction printf n'est ni erronée ni illégale, puisqu'un pointeur est en fait du type int. Imaginez les problèmes que vous pourriez causer si vous utilisiez sprintf() à la place et si vous assigniez ce "tableau de caractères" bien trop long à une autre variable, qui ne dispose que d'un espace limité. Vous finirez très probablement par écrire sur quelque chose d'autre dans la mémoire et vous ferez planter votre programme (si vous avez de la chance).

Oh, et si vous n'assignez pas une valeur de chaîne de caractères au tableau de chars / pointeur lorsque vous le déclarez, vous DEVEZ lui allouer une quantité suffisante de mémoire avant de lui donner une valeur. En utilisant malloc, calloc ou similaire. Ceci puisque vous n'avez déclaré qu'un seul élément dans votre tableau / une seule adresse mémoire à pointer. Voici donc quelques exemples :

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

Notez que vous pouvez toujours utiliser la variable x après avoir effectué un free() de la mémoire allouée, mais vous ne savez pas ce qu'elle contient. Notez également que les deux printf() peuvent vous donner des adresses différentes, car il n'y a aucune garantie que la deuxième allocation de mémoire soit effectuée dans le même espace que la première.

10 votes

Votre exemple avec 120 n'est pas correct. Il utilise %c et non %s, il n'y a donc pas de bogue ; il imprime simplement la lettre minuscule x. En outre, votre affirmation ultérieure selon laquelle un pointeur est de type int est erronée et constitue un très mauvais conseil à donner à un programmeur C inexpérimenté en matière de pointeurs.

0 votes

@R.. le %c a été remplacé par %s

14 votes

-1 Vous avez bien commencé mais le premier exemple est faux. Non, ce n'est pas la même chose. Dans le premier cas a est un pointeur et dans le second cas a est un tableau. L'ai-je déjà mentionné ? Ce n'est pas la même chose ! Vérifiez par vous-même : Comparez sizeof(a), essayez d'assigner une nouvelle adresse à un tableau. Cela ne fonctionnera pas.

61voto

trshiv Points 1163

L'une des raisons d'utiliser des pointeurs est qu'une variable ou un objet peut être modifié dans une fonction appelée.

En C++, il est préférable d'utiliser des références plutôt que des pointeurs. Bien que les références soient essentiellement des pointeurs, le C++ masque dans une certaine mesure ce fait et donne l'impression que l'on passe par valeur. Cela permet de modifier facilement la façon dont la fonction appelante reçoit la valeur sans avoir à modifier la sémantique de la transmission.

Considérez les exemples suivants :

Utilisation de références :

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

Utilisation des pointeurs :

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}

27 votes

Il est probablement judicieux de mentionner que les références sont plus sûres, en ce sens qu'il n'est pas possible de passer une référence nulle.

36 votes

Oui, c'est probablement le plus grand avantage de l'utilisation de références. Merci de l'avoir souligné. Sans mauvais jeu de mots :)

2 votes

Vous avez sûrement peut transmettre une référence nulle. Ce n'est pas aussi simple que de passer un pointeur nul.

50voto

Kyle Cronin Points 35834
  1. Les pointeurs vous permettent de faire référence au même espace de la mémoire à partir de plusieurs emplacements. Cela signifie que vous pouvez mettre à jour la mémoire à un endroit et que le changement peut être vu à partir d'un autre endroit dans votre programme. Vous gagnerez également de l'espace en étant capable de partager des composants dans vos structures de données.
  2. Vous devez utiliser les pointeurs chaque fois que vous devez obtenir et transmettre l'adresse d'un endroit spécifique de la mémoire. Vous pouvez également utiliser des pointeurs pour naviguer dans des tableaux :
  3. Un tableau est un bloc de mémoire contiguë qui a été alloué avec un type spécifique. Le nom du tableau contient la valeur du point de départ du tableau. Lorsque vous ajoutez 1, cela vous amène au deuxième point. Cela vous permet d'écrire des boucles qui incrémentent un pointeur qui glisse vers le bas du tableau sans avoir un compteur explicite à utiliser pour accéder au tableau.

Voici un exemple en C :

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

imprime

ifmmp

car il prend la valeur de chaque caractère et l'incrémente de un.

1 votes

because it takes the value for each character and increments it by one . Est-il représenté en ascii ou comment ?

0 votes

Serait p=0; réinitialiser le pointeur ?

37voto

Bill the Lizard Points 147311

Les pointeurs sont un moyen d'obtenir une référence indirecte à une autre variable. Au lieu de contenir la valeur d'une variable, ils vous indiquent sa adresse . Ceci est particulièrement utile lorsqu'il s'agit de tableaux, car en utilisant un pointeur sur le premier élément d'un tableau (son adresse), vous pouvez rapidement trouver l'élément suivant en incrémentant le pointeur (jusqu'à l'adresse suivante).

La meilleure explication des pointeurs et de l'arithmétique des pointeurs que j'ai lue se trouve dans l'ouvrage de K & R. Le langage de programmation C . Un bon livre pour commencer à apprendre le C++ est Introduction au C++ .

1 votes

Merci ! enfin une explication pratique des avantages de l'utilisation de la pile ! un pointeur sur une position dans un tableau améliore-t-il également les performances d'accès aux valeurs @ et relatives au pointeur ?

28voto

C.. Points 10739

Laissez-moi essayer de répondre à cette question aussi.

Les pointeurs sont similaires aux références. En d'autres termes, ils ne sont pas des copies, mais plutôt un moyen de se référer à la valeur originale.

Pourquoi utiliser des pointeurs plutôt que des variables normales ? La réponse devient plus claire lorsque vous avez affaire à des types complexes, comme les classes, les structures et les tableaux. Si vous utilisiez une variable normale, vous pourriez finir par faire une copie (les compilateurs sont suffisamment intelligents pour empêcher cela dans certaines situations et C++11 aide aussi, mais nous resterons à l'écart de cette discussion pour le moment).

Maintenant, que se passe-t-il si vous voulez modifier la valeur originale ? Vous pourriez utiliser quelque chose comme ceci :

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

Cela fonctionnera très bien et si vous ne savez pas exactement pourquoi vous utilisez des pointeurs, vous ne devriez pas les utiliser. Méfiez-vous de la raison "ils sont probablement plus rapides". Faites vos propres tests et s'ils sont réellement plus rapides, utilisez-les.

Cependant, disons que vous résolvez un problème où vous devez allouer de la mémoire. Lorsque vous allouez de la mémoire, vous devez la désallouer. L'allocation de mémoire peut réussir ou échouer. C'est là que les pointeurs sont utiles : ils vous permettent de vérifier l'existence de l'objet que vous avez alloué et d'accéder à l'objet pour lequel la mémoire a été allouée en déréférençant le pointeur.

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

C'est la raison pour laquelle vous utilisez des pointeurs - les références supposent que l'élément auquel vous faites référence existe déjà. Un pointeur ne le fait pas.

L'autre raison pour laquelle vous utiliseriez des pointeurs (ou du moins finiriez par avoir affaire à eux) est qu'il s'agit d'un type de données qui existait avant les références. Par conséquent, si vous finissez par utiliser des bibliothèques pour faire les choses pour lesquelles vous savez qu'elles sont meilleures, vous constaterez que beaucoup de ces bibliothèques utilisent des pointeurs partout, simplement en raison de leur ancienneté (beaucoup d'entre elles ont été écrites avant C++).

Si vous n'utilisiez aucune bibliothèque, vous pourriez concevoir votre code de manière à éviter les pointeurs, mais étant donné que les pointeurs sont l'un des types de base du langage, plus vite vous serez à l'aise avec leur utilisation, plus vos compétences en C++ seront portables.

Du point de vue de la maintenabilité, je dois également mentionner que lorsque vous utilisez des pointeurs, vous devez soit tester leur validité et gérer le cas où ils ne sont pas valides, soit supposer qu'ils sont valides et accepter le fait que votre programme se plantera ou pire SI cette supposition est brisée. En d'autres termes, votre choix avec les pointeurs est soit d'introduire une complexité de code ou un effort de maintenance supplémentaire ; quelque chose se casse et vous essayez de traquer un bogue qui appartient à toute une classe d'erreurs que les pointeurs introduisent, comme la corruption de mémoire.

Donc, si vous contrôlez l'ensemble de votre code, évitez les pointeurs et utilisez plutôt des références, en les maintenant constantes lorsque vous le pouvez. Cela vous obligera à penser à la durée de vie de vos objets et rendra votre code plus facile à comprendre.

N'oubliez pas cette différence : Une référence est essentiellement un pointeur valide. Un pointeur n'est pas toujours valide.

Je suis donc en train de dire qu'il est impossible de créer une référence invalide ? Non. C'est tout à fait possible, car le C++ vous permet de faire presque tout. C'est juste plus difficile à faire involontairement et vous serez étonné de voir combien de bugs sont involontaires :)

0 votes

Il est possible d'écrire de belles classes d'enveloppe pour l'IO en mémoire, ce qui permet d'éviter l'utilisation de pointeurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X