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


58

Eh bien, je comprends fondamentalement comment utiliser les pointeurs, mais pas la meilleure façon de les utiliser afin de faire une meilleure programmation.

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


Demandez quels problèmes de programmation sont mieux résolus en utilisant des pointeurs, et non quels projets / programmes. Vous pouvez écrire un programme en utilisant des pointeurs ou non - ils (les projets) sont tout simplement une construction trop grande pour une réponse significative.
Oded

37
Les problèmes de programmation sont créés à l'aide de pointeurs et non résolus. :)
xpda

4
En fait, je me demande quels problèmes peuvent être résolus sans pointeur. Très peu à coup sûr.
deadalnix

4
@deadalnix, cela dépend si vous parlez de pointeurs explicites ou implicites. Si vous parlez de pointeurs implicites (c’est-à-dire que des pointeurs sont utilisés quelque part), il serait alors impossible d’écrire un programme utilisant la pile ou le tas. Si vous parlez de pointeurs explicites, alors vous n'êtes pas restreint, vous pouvez faire n'importe quelle taille d'allocation en créant de nouvelles trames de pile (en utilisant essentiellement un appel de fonction) et une désallocation en utilisant des appels finaux, en supposant qu'ils soient optimisés par le compilateur.
dan_waterworth

3
ugh, certaines de ces réponses sont affreuses.

Réponses:


68

Manipuler de grandes quantités de données en mémoire est le point fort des pointeurs.

Passer un gros objet par référence équivaut à ne faire que passer un ancien numéro ordinaire. Vous pouvez manipuler directement les pièces nécessaires, par opposition à la copie d'un objet, à sa modification, puis à la restitution de la copie à replacer à la place de l'original.


12
C'est une excellente réponse et j'ajouterais que vous n'avez pas besoin d'objets volumineux pour manipuler de grandes quantités de données; de temps en temps manipuler de gros objets le fera, mais un problème encore plus commun est de manipuler un tas de petits objets très fréquemment. Ce qui, si vous y réfléchissez, concerne à peu près tous les programmes informatiques.
Jhocking

44

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 à la place 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 l'ensemble des données associées à l'enregistrement.

  3. Paramètres de
    transmission Dans ce cas, vous transmettez l'adresse des données à la place des données elles-mêmes. Encore une fois, pensez à un algorithme de compression de nom qui fonctionne sur des millions de lignes.

Le concept peut être étendu aux structures de données telles que les bases de données relationnelles dans lesquelles un pointeur s'apparente à une clé étrangère . Certaines langues 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 post suivant peut être pertinent d'une certaine manière:


14
Je ne dirais pas que C # n'encourage pas l'utilisation de pointeurs. au lieu de cela, cela n'encourage pas l'utilisation de pointeurs non gérés - de nombreuses informations en C # sont transmises par référence plutôt que par valeur.
Blakomen

Bien, bonne explication
Sabby le

5
Il a fallu que le commentaire C # soit négatif. Les "références" en C # sont équivalentes aux pointeurs, c'est simplement que C # vous empêche de savoir que vous traitez avec un pointeur sur un objet du tas géré.
MattDavey

1
@ MattDavey: les références C # ne sont pas des pointeurs. Vous ne pouvez pas ajouter à partir d'eux, les utiliser pour indexer un tableau, utiliser plusieurs indirection, etc. Vous ne pouvez pas avoir de références à des références, par exemple.
Billy ONeal

5
@Billy ONeal que l'idée que "ce n'est qu'un indicateur si vous pouvez faire du calcul" est franchement ridicule, mais je n'ai pas la patience de discuter avec vous.
MattDavey

29

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 chaînées (simples, doubles et circulaires), les arbres (rouge-noir, AVL, trie, binaire, partitionnement d'espaces…) et les graphes sont tous des exemples de structures qui peuvent être construites le plus naturellement en termes de références plutôt qu'en termes de valeurs .


Vous oubliez la liste chaînée XOR [=
dan_waterworth

@ dan_waterworth: Non. Je connais très bien cette magie noire.
Jon Purdy

8

Un moyen simple est le polymorphisme. Le polymorphisme ne fonctionne qu'avec les pointeurs.

En outre, vous utilisez des pointeurs chaque fois que vous avez besoin d'une allocation de mémoire dynamique. En C, cela se produit généralement lorsque vous devez stocker des données dans un tableau sans connaître leur taille au moment de la compilation. Vous appelez ensuite malloc pour allouer la mémoire et un pointeur pour y accéder. En outre, 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 tout un tableau en 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 devez 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 indicateurs:

  1. Trouvez des projets qui jouent avec les c-strings et les tableaux traditionnels.
  2. Trouvez des projets utilisant l'héritage.

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

Lisez deux matrices nxn à partir d’un fichier, effectuez les opérations de base relatives à l’espace vectoriel 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 disposerez de deux tableaux de tableaux (tableaux dynamiques multidimensionnels). Une fois ce projet terminé, vous aurez une très bonne idée de l’utilisation des pointeurs.


2
"Le polymorphisme ne fonctionne qu'avec les pointeurs." Non précis: le polymorphisme fonctionne également avec des références. Qui sont, en fin de compte, des indicateurs, mais je pense que la distinction est importante, en particulier pour les débutants.
Laurent Pireyn le

votre exemple de copie d'un tableau sur une ligne sert en réalité à copier une chaîne terminée par zéro dans une ligne, pas un tableau général.
Tcrosley

8

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 à foosortir du champ d'application, la variable l'est également f.

Cela devient un problème lorsque vous devez renvoyer une fonction autre qu'un type intégral (par exemple, la structure Foo de l'exemple ci-dessus).

Par exemple, la fonction suivante entraînerait un "comportement non défini".

struct Foo {
  int bar, baz
};

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

Si vous voulez retourner quelque chose comme struct Foo *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 mallocalloue un objet sur le tas et renvoie un pointeur sur cet objet. Notez que le terme "objet" est utilisé ici de manière vague, ce qui signifie "quelque chose" plutôt qu'un objet dans le sens d'une programmation orientée objet.

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

Edit : j'ai omis de remarquer que cette question est étiquetée comme une question C ++. Les opérateurs C ++ newet new[]remplissent la même fonction que malloc. Les opérateurs deleteet delete[]sont analogues à free. Alors que newet deletedevrait être utilisé exclusivement pour allouer et libérer des objets C ++, l'utilisation de mallocet freeest parfaitement légale dans le code C ++.


1
(+1) également connu sous le nom de "variables allouées statiques" et "variables allouées dynamiques" ;-)
umlcat

4

Ecrivez tout projet non-trivial en C et vous devrez comprendre comment / quand utiliser les pointeurs. En C ++, vous utiliserez principalement des objets compatibles RAII qui gèrent les pointeurs en interne, mais en C raw, les pointeurs ont un rôle beaucoup plus important. En ce qui concerne le type de projet que vous devriez faire, cela peut être quelque chose de non trivial:

  • Un serveur web minuscule et simple
  • Outils de ligne de commande Unix (less, cat, sort, etc.)
  • Certains projets que vous souhaitez réaliser pour votre propre intérêt et pas uniquement pour apprendre

Je recommande le dernier.


Donc, je vais juste pour un projet que je veux faire (comme un jeu en 2D) et c'est supposé que j'aurai besoin et apprendre des pointeurs?
Dysoco

3
D'après mon expérience, la meilleure façon d'apprendre consiste à faire de vrais projets qui sont légèrement hors de portée. Les concepts peuvent être appris en lisant et en écrivant de petits programmes "jouets". Cependant, pour vraiment savoir comment utiliser ces concepts pour écrire de meilleurs programmes, il vous suffit d'écrire des programmes non-jouets.
Viktor Dahl

3

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

Les pointeurs en tant qu’implémentation de bas niveau spécifique des références, où vous pouvez manipuler directement les adresses mémoire, sont très puissants, mais peuvent être légèrement dangereux à utiliser (par exemple, pointez sur des emplacements mémoire en dehors du programme).

L'avantage d'utiliser des pointeurs directement, c'est qu'ils seront un peu plus rapides s'ils n'ont pas à faire de vérifications de sécurité. Les langages tels que 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.

Pour ce qui est de la raison pour laquelle vous avez besoin d’indirection, la liste est assez longue, mais les deux idées principales sont essentiellement les suivantes:

  1. La copie de valeurs de gros objets est lente et l’objet sera stocké deux fois dans la RAM (potentiellement très onéreuse), mais la copie par référence est quasi instantanée et n’utilise que quelques octets de RAM (pour l’adresse). Par exemple, supposons que vous ayez environ 1 000 gros objets (chacun représentant environ 1 Mo de RAM) en mémoire et que votre utilisateur ait besoin de pouvoir sélectionner l'objet actuel (sur lequel l'utilisateur agira). Avoir une variable selected_objectqui fait référence à l'un des objets est beaucoup plus efficace que de copier la valeur de l'objet actuel dans une nouvelle variable.
  2. Disposer de structures de données complexes faisant référence à d'autres objets, tels que 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 de la structure de données signifie que vous ne devez 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 (vous pouvez avoir des insertions de temps constant).

2

La manipulation d’images au niveau des pixels est presque toujours plus facile et plus rapide avec des pointeurs. Parfois, il est uniquement possible d'utiliser des pointeurs.


1
Je ne pense pas que l'affirmation "Parfois, il est seulement possible d'utiliser des pointeurs" est vraie. Il existe des langages qui n'exposent pas les pointeurs au développeur, pourtant ils peuvent être utilisés pour résoudre des problèmes dans le même espace.
Thomas Owens

Peut-être ... Je n'ai pas la connaissance de toutes les langues disponibles, mais je ne voudrais certainement pas écrire une routine pour appliquer un filtre de convolution à une image en utilisant une langue qui n'a pas de pointeur.
Dave Nay

@ Thomas, tant que vous avez raison, la question concerne c ++, qui expose les pointeurs au développeur.
Jonathan Henson

@ Jonathan Malgré tout, si vous pouvez résoudre un problème dans une langue qui n'expose pas les pointeurs, vous pouvez également résoudre ce problème dans une langue qui expose les pointeurs sans utiliser de pointeurs. Cela rend la première phrase vraie, mais la deuxième phrase, fausse - à ma connaissance, il n’existe aucun problème qui ne puisse être résolu sans l'utilisation de pointeurs.
Thomas Owens

1
Au contraire, le traitement des images implique souvent une "arithmétique des coordonnées", en prenant le sinus et le cosinus d'un angle et en le multipliant par les coordonnées x et y. Dans d'autres cas, une réplication des pixels en boucle ou hors-frontière est nécessaire. En ce sens, l'arithmétique de pointeur est plutôt inadéquate.
Rwong

1

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

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


1

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 vitaux, je vous conseillerais alors d'apprendre ce qu'est une liste chaînée et d'essayer de l'écrire sans utiliser de pointeur. Je ne vous ai pas posé de défi impossible (CONSEIL: les pointeurs servent à référencer des emplacements en mémoire, comment référencez-vous des éléments dans des tableaux?).


+1 pour mentionner les structures de données, l'utilisation dans les algorithmes est une conséquence naturelle.
Matthieu M. le

0

Comme exemple concret, la construction d’un carnet d’ordres limité.

Le flux ITCH 4.1, par exemple, a un concept de "remplacer" les 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 à deux extrémités avec des pointeurs rend l'opération très facile.


0

En parcourant les différents sites StackExchange, je me rends compte qu'il 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é à troll ou flamber, 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. Presque tout ce que ça dit c'est "je ne sais pas C." Si j’analyse un peu plus loin et plus cyniquement, la suite de cette "question" est claire: "Y at-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 peut remplacer le simple fait de comprendre le concept sous-jacent et son utilisation.

J'ai l'impression qu'il est plus constructif d'aller bien apprendre le C que d'aller sur le Web et de demander aux gens ceci. Lorsque vous savez que vous ne vous poserez plus de questions comme celle-ci, ce sera comme si vous demandiez: "Quels sont les meilleurs problèmes à résoudre en utilisant une brosse à dents?"


0

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

Le pointeur est absolument essentiel pour la programmation dite dynamique , c’est quand vous ne savez pas combien de mémoire vous aurez besoin avant que votre programme ne s’exécute. En programmation dynamique, vous pouvez demander des fragments 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 utiliser ces fragments.

Si 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., en fonction de la structure) et que chaque bloc contient des références à un ou plusieurs autres blocs. Ces structures "liées" construisent la majorité des types de données modernes: graphes, arbres, listes, etc.

  2. Vous pouvez programmer en utilisant le paradigme OOP (Object-Oriented Programming). La totalité de la POO est basée sur l’utilisation non pas de variables directes, mais de références à des instances de classe (appelées objets) et de leur manipulation. Aucune instance unique ne peut exister sans pointeurs (même s'il est possible d'utiliser des classes statiques uniquement même sans pointeurs, il s'agit plutôt d'une exception).


0

Drôle, je viens de répondre à une question sur le C ++ et de parler de pointeurs.

La version courte est que vous n'avez JAMAIS besoin de pointeurs, sauf si 1) la bibliothèque que vous utilisez 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., placez-le simplement sur la pile et utilisez un objet stl. Le renvoi 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 copie les données que si vous y écrivez. C ++ n'est pas le nouveau C ++ 11, ce qui facilitera la tâche des rédacteurs de bibliothèques.

Votre question pourrait être répondu à cette partie

Si vous utilisez un pointeur, assurez-vous qu'il est dans l'une de ces deux conditions. 1) Vous transmettez une entrée qui peut être nullable. Un exemple est un nom de fichier optionnel. 2) Si vous voulez céder la propriété. Comme si vous passiez ou retourniez le pointeur, il ne vous reste plus aucune copie, ni 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 va très vite.

NOTE ADDITIONNELLE: Je remarque que j'écris mes propres structures et que je les transmets. Alors, comment puis-je faire cela sans utiliser de pointeurs? Ce n'est pas un conteneur STL, donc passer par réf est lent. Je charge toujours ma liste de données / deques / cartes et autres. Je ne me souviens pas avoir renvoyé d'objets à moins que ce ne soit une sorte de liste / carte. Pas même une ficelle. J'ai regardé le code pour les objets simples 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 (plusieurs) ou une pré-allocation / passe dans une référence à remplir (un seul).

Maintenant, si vous écrivez, vous avez votre propre deque, carte, etc. vous devrez utiliser des pointeurs. Mais vous n'en avez pas besoin. Laissons STL et éventuellement stimuler l'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 face aux libs qui vous obligent à


0

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 la MMU l'a mappée dans l'espace mémoire du système.


0

Je vois des pointeurs comme mon index, nous avions l'habitude de faire quelques choses:

  1. quand quelqu'un vous demande quelque chose que vous ne pouvez pas porter, comme où est la rue, untel, je "pointe" cette rue, et c'est ce que nous faisons en cas de litige
  2. en comptant ou en parcourant quelque chose, nous utiliserions le même doigt, et c'est ce que nous faisons dans des tableaux

s'il vous plaît pardonnez cette mauvaise réponse


0

Comme votre question porte la mention C ++, je vais répondre à votre question pour cette langue.

En C ++, il existe une distinction entre les pointeurs et les références. Par conséquent, il existe deux scénarios dans lesquels des pointeurs (ou des pointeurs intelligents) sont nécessaires pour faciliter certains comportements. Ils peuvent être utilisés dans d'autres circonstances, mais vous demandez comment "mieux les utiliser", 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 pointé par 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 plutôt que sur 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 ont été créés.

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


Peut-être que quelqu'un préférerait trancher un objet plutôt que d'utiliser un pointeur. Bon débogage: D
deadalnix le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.