web-dev-qa-db-fra.com

Quels problèmes de programmation sont mieux résolus en utilisant des pointeurs?

Eh bien, je comprends essentiellement comment utiliser les pointeurs, mais pas comment les utiliser au mieux pour faire une meilleure programmation.

Quels sont les bons projets ou problèmes à résoudre impliquant l'utilisation de pointeurs pour que je puisse mieux les comprendre?

58
dysoco

La manipulation de grandes quantités de données en mémoire est l'endroit où les pointeurs brillent vraiment.

Passer un grand objet par référence équivaut à simplement passer le long d'un ancien numéro simple. Vous pouvez manipuler les pièces nécessaires directement au lieu de copier un objet, de le modifier, puis de renvoyer la copie à mettre à la place de l'original.

68
user7007

Le concept de pointeur vous permet de faire référence aux données par adresse sans dupliquer le stockage des données. Cette approche permet d'écrire des algorithmes efficaces tels que:

  1. Tri
    Lorsque vous déplacez des données dans un algorithme de tri, vous pouvez déplacer le pointeur au lieu des données elles-mêmes - pensez à trier des millions de lignes sur une chaîne de 100 caractères; vous enregistrez beaucoup de mouvements de données inutiles.

  2. Listes liées
    Vous pouvez stocker l'emplacement de l'élément suivant et/ou précédent et non toutes les données associées à l'enregistrement.

  3. Passage des paramètres
    Dans ce cas, vous transmettez l'adresse des données au lieu des données elles-mêmes. Encore une fois, pensez à un algorithme de compression de nom qui s'exécute sur des millions de lignes.

Le concept peut être étendu à des structures de données telles que des bases de données relationnelles où un pointeur s'apparente à clé étrangère . Certains langages n'encouragent pas l'utilisation de pointeurs tels que C # et COBOL.

Des exemples peuvent être trouvés dans de nombreux endroits tels que:

Le message suivant peut être pertinent d'une manière ou d'une autre:

44
NoChance

Je suis surpris qu'aucune autre réponse n'ait mentionné cela: les pointeurs vous permettent de créer des structures de données non contiguës et non linéaires, dans lesquelles un élément peut être lié à plusieurs autres de manière complexe.

Les listes liées (liées individuellement, doublement et circulairement), les arbres (rouge-noir, AVL, trie, binaire, partitionnement d'espace…) et les graphiques sont tous des exemples de structures qui peuvent être construites le plus naturellement en termes de références plutôt que de simples valeurs .

29
Jon Purdy

Une façon simple est dans le polymorphisme. Le polymorphisme ne fonctionne qu'avec des pointeurs.

En outre, vous utilisez des pointeurs chaque fois que vous avez besoin d'une allocation dynamique de mémoire. En C, cela se produit généralement lorsque vous devez stocker des données dans un tableau mais que vous ne connaissez pas la taille au moment de la compilation. Vous appelleriez alors malloc pour allouer la mémoire et un pointeur pour y accéder. Aussi, que vous le sachiez ou non, lorsque vous utilisez un tableau, vous utilisez des pointeurs.

for(int i = 0; i < size; i++)
   std::cout << array[i];

est l'équivalent de

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

Cette connaissance vous permet de faire des choses vraiment cool comme copier un tableau entier sur une seule ligne:

while( (*array1++ = *array2++) != '\0')

En c ++, vous utilisez new pour allouer de la mémoire à un objet et le stocker dans un pointeur. Vous faites cela chaque fois que vous avez besoin de créer un objet pendant l'exécution plutôt que pendant la compilation (c'est-à-dire qu'une méthode crée un nouvel objet et le stocke dans une liste).

Pour mieux comprendre les pointeurs:

  1. Trouvez des projets qui jouent avec les cordes et les tableaux traditionnels.
  2. Trouvez des projets qui utilisent l'héritage.

  3. Voici le projet sur lequel je me suis coupé les dents:

Lisez deux matrices n x n à partir d'un fichier et effectuez les opérations d'espace vectoriel de base sur celles-ci et imprimez leur résultat à l'écran.

Pour ce faire, vous devez utiliser des tableaux dynamiques et vous référer à vos tableaux par des pointeurs car vous aurez deux tableaux de tableaux (tableaux dynamiques multidimensionnels). Après avoir terminé ce projet, vous aurez une assez bonne idée de l'utilisation des pointeurs.

8
Jonathan Henson

Pour vraiment comprendre pourquoi les pointeurs sont importants, vous devez comprendre la différence entre l'allocation de tas et l'allocation de pile.

Voici un exemple d'allocation de pile:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

Les objets alloués sur la pile n'existent que pendant la durée de l'exécution de la fonction en cours. Lorsque l'appel à foo sort du domaine d'application, la variable f fait de même.

Un cas où cela devient un problème est lorsque vous devez renvoyer autre chose qu'un type intégral à partir d'une fonction (par exemple la structure Foo de l'exemple ci-dessus).

Par exemple, la fonction suivante entraînerait ce que l'on appelle un "comportement non défini".

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

Si vous voulez renvoyer quelque chose comme struct Foo * À partir d'une fonction, vous avez vraiment besoin d'une allocation de tas:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

La fonction malloc alloue un objet sur le tas et renvoie un pointeur sur cet objet. Notez que le terme "objet" est utilisé ici de manière lâche, signifiant "quelque chose" plutôt que objet dans le sens d'une programmation orientée objet.

La durée de vie des objets alloués en tas est contrôlée par le programmeur. La mémoire de cet objet sera réservée jusqu'à ce que le programmateur le libère, c'est-à-dire en appelant free() ou jusqu'à ce que le programme se termine.

Modifier : je n'ai pas remarqué que cette question est étiquetée comme une question C++. Les opérateurs C++ new et new[] Remplissent la même fonction que malloc. Les opérateurs delete et delete[] Sont analogues à free. Alors que new et delete doivent être utilisés exclusivement pour allouer et libérer des objets C++, l'utilisation de malloc et free est parfaitement légale dans le code C++.

8
Mike Steinert

Écrivez n'importe quel projet non trivial en C et vous devrez comprendre comment/quand utiliser des pointeurs. En C++, vous utiliserez principalement des objets compatibles RAII qui gèrent les pointeurs en interne, mais en C, les pointeurs bruts ont un rôle beaucoup plus répandu. Quant au type de projet que vous devez faire, il peut être quelque chose de non trivial:

  • Un serveur Web minuscule et simple
  • Outils de ligne de commande Unix (less, cat, sort etc.)
  • Un projet réel que vous voulez faire pour lui-même et pas seulement pour apprendre

Je recommande le dernier.

4
Viktor Dahl

Presque tous les problèmes de programmation qui peuvent être résolus avec des pointeurs peuvent être résolus avec d'autres types plus sûrs de références (ne faisant pas référence à références C++ , mais le concept CS général d'avoir une référence de variable à la valeur des données stockées ailleurs).

Pointeurs en étant une implémentation spécifique de bas niveau de références, où vous pouvez manipuler directement les adresses mémoire sont très puissantes, mais peuvent être légèrement dangereuses à utiliser (par exemple, pointer vers des emplacements mémoire en dehors du programme).

L'avantage d'utiliser directement des pointeurs est qu'ils seront légèrement plus rapides en n'ayant pas à effectuer de contrôles de sécurité. Les langages comme Java qui n'implémentent pas directement les pointeurs de style C subiront une légère baisse de performances, mais réduiront de nombreux types de situations difficiles à déboguer.

Quant à savoir pourquoi vous avez besoin d'indirection, la liste est assez longue, mais essentiellement les deux idées clés sont:

  1. La copie des valeurs de gros objets est lente et fera que l'objet sera stocké dans RAM deux fois (potentiellement très coûteux), mais la copie par référence est presque instantanée en utilisant seulement quelques octets de RAM (pour l'adresse). Par exemple, supposons que vous ayez ~ 1000 gros objets (chacun d'environ 1 Mo de RAM) en mémoire, et votre utilisateur doit être en mesure de sélectionner l'objet actuel (sur lequel on agira) par l'utilisateur). Avoir une variable selected_object qui fait référence à l'un des objets est beaucoup plus efficace que de copier la valeur de l'objet courant dans une nouvelle variable.
  2. Avoir des structures de données compliquées qui se réfèrent à d'autres objets comme des listes liées ou des arbres, où chaque élément de la structure de données fait référence à d'autres éléments de la structure de données. L'avantage de faire référence à d'autres éléments dans la structure de données signifie que vous n'avez pas à déplacer chaque élément de la liste en mémoire simplement parce que vous avez inséré un nouvel élément au milieu de la liste (peut avoir des insertions de temps constant).
3
dr jimbob

La manipulation d'images au niveau des pixels est presque toujours plus facile et plus rapide à l'aide de pointeurs. Parfois, cela n'est possible qu'en utilisant des pointeurs.

2
Dave Nay

Les pointeurs sont une partie essentielle de toute implémentation de structure de données en C et les structures de données sont une partie essentielle de tout programme non trivial.

Si vous souhaitez savoir pourquoi les pointeurs sont si importants, je vous suggère d'apprendre ce qu'est une liste chaînée et d'essayer d'en écrire une sans utiliser de pointeurs. Je ne vous ai pas posé de défi impossible (CONSEIL: les pointeurs sont utilisés pour référencer des emplacements en mémoire, comment référencez-vous les choses dans les tableaux?).

1
dan_waterworth

Les pointeurs sont utilisés dans de nombreux langages de programmation sous la surface sans déranger l'utilisateur à ce sujet. C/C++ vous donne juste accès à eux.

Quand les utiliser: aussi souvent que possible, car la copie des données est inefficace. Quand ne pas les utiliser: lorsque vous voulez deux copies qui peuvent être modifiées individuellement. (Ce qui finira par copier le contenu de object_1 vers un autre endroit de la mémoire et retourner un pointeur - cette fois en pointant sur object_2)

1
mmlac

En parcourant les différents sites StackExchange, je me rends compte que c'est plutôt en vogue de poser une question comme celle-ci. Au risque de critiques et de votes négatifs, je serai honnête. Ce n'est pas destiné à troller ou à enflammer, je veux seulement aider, en donnant une évaluation honnête de la question.

Et cette évaluation est la suivante: C'est une question très étrange à poser à un programmeur C. À peu près tout ce qu'il dit est "je ne sais pas C." Si j'analyse un peu plus loin et plus cyniquement, il y a une nuance de suivi à cette "question": "Y a-t-il un raccourci que je peux prendre pour acquérir rapidement et soudainement les connaissances d'un programmeur C expérimenté, sans consacrer de temps pour une étude indépendante? " Toute "réponse" que quelqu'un peut donner ne remplace pas le fait d'aller et de faire les démarches nécessaires pour comprendre le concept sous-jacent et son utilisation.

J'ai l'impression qu'il est plus constructif d'aller bien apprendre le C, de première main, que d'aller sur le Web et de demander cela aux gens. Lorsque vous connaissez bien C, vous n'aurez pas la peine de poser des questions comme celle-ci, ce sera comme si vous vous demandiez "quels sont les meilleurs problèmes à résoudre avec une brosse à dents?

0
asveikau

Même si les pointeurs brillent vraiment lorsque vous travaillez avec des objets mémoire volumineux, il existe toujours un moyen de faire de même sans eux.

Le pointeur est absolument essentiel pour ce qu'on appelle programmation dynamique, c'est quand vous ne savez pas de combien de mémoire vous aurez besoin avant que votre programme ne s'exécute. Dans la programmation dynamique, vous pouvez demander des morceaux de mémoire pendant l'exécution et y placer vos propres données - vous avez donc besoin d'un pointeur ou de références (la différence n'est pas importante ici) pour pouvoir travailler avec ces morceaux de données.

Tant que vous pouvez réclamer une certaine mémoire pendant l'exécution et placer vos données dans la mémoire nouvellement acquise, vous pouvez alors effectuer les opérations suivantes:

  1. Vous pouvez avoir des structures de données auto-extensibles. Ce sont des structures qui peuvent s'étendre en réclamant de la mémoire supplémentaire tant que leur capacité est épuisée. La propriété clé de chaque structure auto-extensible est qu'elle se compose de petits blocs de mémoire (appelés nœuds, éléments de liste, etc. selon la structure) et chaque bloc contient des références à d'autres blocs. Ces structures "liées" construisent la majorité des types de données modernes: graphiques, arbres, listes, etc.

  2. Vous pouvez programmer en utilisant le paradigme OOP (Object-Oriented Programming). L'ensemble OOP est basé sur l'utilisation non pas de variables directes, mais de références à des instances de classe (objets appelés ) et aucune manipulation ne peut exister sans pointeurs (même s'il est possible d'utiliser des classes statiques uniquement même sans pointeurs, c'est plutôt une exception).

0
Alexander Galkin

C'est drôle, je viens de répondre à une question sur C++ et j'ai parlé de pointeurs.

La version courte est que vous n'avez JAMAIS besoin de pointeurs à moins que 1) la bibliothèque que vous utilisez ne vous oblige 2) Vous avez besoin d'une référence nullable.

Si vous avez besoin d'un tableau, d'une liste, d'une chaîne, etc., disposez-le simplement sur la pile et utilisez un objet stl. Le retour ou le passage d'objets stl sont rapides (fait non vérifié) car ils ont un code interne qui copie un pointeur au lieu d'un objet et ne copiera les données que si vous y écrivez. C'est du C++ normal, pas même le nouveau C++ 11 qui le rendra plus facile pour les écrivains de bibliothèque.

On pourrait répondre à votre question dans cette partie

Si vous utilisez un pointeur, assurez-vous qu'il se trouve dans l'une de ces deux conditions. 1) Vous passez une entrée qui peut être annulée. Un exemple est un nom de fichier facultatif. 2) Si vous souhaitez céder la propriété. Comme si vous passez ou renvoyez le pointeur, vous n'en avez AUCUNE copie ni n'utilisez le pointeur que vous donnez

ptr=blah; func(ptr); //never use ptr again for here on out.

Mais je n'ai pas utilisé de pointeurs ou de pointeurs intelligents depuis très longtemps et j'ai profilé mon application. Ça tourne très vite.

NOTE ADDITIONNELLE: Je remarque que j'écris mes propres structures et je les fais circuler. Alors, comment puis-je faire cela sans utiliser de pointeurs? Ce n'est pas un conteneur STL, donc passer par ref est lent. Je charge toujours ma liste de données/deques/maps et autres. Je ne me souviens pas avoir retourné d'objets sauf s'il s'agissait d'une sorte de liste/carte. Pas même une chaîne. J'ai regardé du code pour des objets uniques et je remarque que je fais quelque chose comme ceci { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; } donc je retourne à peu près des objets (multiples) ou pré-alloue/passe une référence à remplir (unique).

Maintenant, si vous écriviez votre propre carte, carte, etc., vous devrez utiliser des pointeurs. Mais vous n'en avez pas besoin. Laissez STL et peut-être augmenter votre inquiétude. Vous avez juste besoin d'écrire des données et des solutions. Pas de conteneurs pour les contenir;)

J'espère que vous n'utilisez plus de pointeurs: D. Bonne chance pour les libs qui vous obligent à

0
user2528

Je vois les pointeurs comme mon index, nous utilisons pour faire quelques choses:

  1. quand quelqu'un vous demande quelque chose que vous ne pouvez pas transporter, comme où se trouve telle ou telle rue, je "pointe" sur cette rue, et c'est ce que nous faisons en cas d'arguments par référence
  2. lorsque nous comptons ou traversons quelque chose, nous utilisons le même doigt, et c'est ce que nous faisons dans les tableaux

veuillez pardonner cette mauvaise réponse

0
A.Rashad

Comme votre question est étiquetée C++, je répondrai à votre question pour ce langage.

En C++, il existe une distinction entre les pointeurs et les références, il existe donc deux scénarios dans lesquels des pointeurs (ou pointeurs intelligents) sont nécessaires pour faciliter certains comportements. Ils peuvent être utilisés dans d'autres circonstances, mais vous demandez comment les utiliser au mieux et dans toutes les autres circonstances, il existe de meilleures alternatives.

1. Polymorphisme

Un pointeur de classe de base vous permet d'appeler une méthode virtuelle qui dépend du type d'objet vers lequel pointe le pointeur.

2. Création d'objets persistants

Les pointeurs sont nécessaires lors de la création dynamique d'un objet (sur le tas au lieu de la pile). Cela est nécessaire lorsque vous souhaitez que la durée de vie des objets soit supérieure à la portée dans laquelle ils sont créés.

En termes de "bons projets ou problèmes à résoudre", comme d'autres l'ont déjà dit ici, tout projet non trivial utilisera des pointeurs.

0
dangerousdave

Les pointeurs sont très utiles pour travailler avec des périphériques mappés en mémoire. Vous pouvez définir une structure qui reflète (par exemple) un registre de contrôle, puis l'affecter à l'adresse du registre de contrôle réel en mémoire et le manipuler directement. Vous pouvez également pointer directement vers un tampon de transfert sur une carte ou une puce si le MMU l'a mappé dans l'espace mémoire du système.

0
TMN

À titre d'exemple concret, créer un carnet de commandes à cours limité.

le flux ITCH 4.1, par exemple, a un concept de "remplacement" des commandes, où les prix (et donc la priorité) peuvent changer. Vous voulez pouvoir prendre des commandes et les déplacer ailleurs. L'implémentation d'une file d'attente à double extrémité avec des pointeurs rend l'opération très facile.

0
Foo Bah