Qu'est-ce que les gens trouvent difficile avec les pointeurs C? [fermé]


173

D'après le nombre de questions publiées ici, il est clair que les gens ont des problèmes fondamentaux lorsqu'ils se familiarisent avec les pointeurs et l'arithmétique des pointeurs.

Je suis curieux de savoir pourquoi. Ils ne m'ont jamais vraiment causé de problèmes majeurs (bien que j'aie appris à leur sujet pour la première fois au Néolithique). Afin d'écrire de meilleures réponses à ces questions, j'aimerais savoir ce que les gens trouvent difficile.

Donc, si vous avez du mal avec les pointeurs, ou si vous l'avez récemment "compris", quels sont les aspects des pointeurs qui vous ont causé des problèmes?


54
Snarky, surestimé, inutilement controversé, répondez avec un grain de vérité: Ils ont été paralysés - mentalement mutilés, je vous le dis - en parlant d'abord de l'un de ces langages "expressifs" de haut niveau. Ils auraient dû commencer par programmer sur du métal nu comme Dieu l'avait voulu Daniel Boone!
dmckee --- ex-modérateur chaton

3
... et Programmer serait mieux car cela se développera en discussion malgré tous vos efforts.
dmckee --- ex-moderator chaton

C'est argumentatif et subjectif, cela génère N résultats, mon difficile est votre facile. Cela fonctionnera probablement sur les programmeurs, mais nous n'y migrons pas encore ces questions car le site n'est pas sorti de la version bêta.
Sam Saffron

2
@Sam Saffron: Bien que je sois généralement d'accord pour dire que c'est plus une question de type programmers.SE, honnêtement, ce ne serait pas mal si les gens étaient prêts à marquer "Je pense que c'est facile" et "Je déteste voir des pointeurs" comme spam elles sont.
jkerian

3
Quelqu'un doit évoquer ceci: "C'est comme un doigt pointé vers la lune. Ne vous concentrez pas sur le doigt ou vous manquerez toute cette gloire céleste" --Bruce Lee
mu est trop court

Réponses:


86

Je soupçonne que les gens vont un peu trop loin dans leurs réponses. Une compréhension de la planification, des opérations réelles du processeur ou de la gestion de la mémoire au niveau de l'assemblage n'est pas vraiment nécessaire.

Lorsque j'enseignais, j'ai trouvé que les lacunes suivantes dans la compréhension des élèves étaient la source de problèmes la plus courante:

  1. Stockage Heap vs Stack. Il est tout simplement stupéfiant de voir combien de personnes ne comprennent pas cela, même dans un sens général.
  2. Empilez des cadres. Juste le concept général d'une section dédiée de la pile pour les variables locales, ainsi que la raison pour laquelle il s'agit d'une `` pile '' ... des détails tels que le stockage de l'emplacement de retour, les détails du gestionnaire d'exceptions et les registres précédents peuvent être laissés en toute sécurité jusqu'à ce que quelqu'un essaie de construire un compilateur.
  3. "La mémoire est la mémoire est la mémoire" La diffusion change simplement les versions des opérateurs ou la quantité d'espace que le compilateur donne pour un morceau de mémoire particulier. Vous savez que vous avez affaire à ce problème lorsque les gens parlent de "ce qu'est réellement la variable (primitive) X ".

La plupart de mes étudiants ont pu comprendre un dessin simplifié d'un morceau de mémoire, généralement la section des variables locales de la pile à la portée actuelle. Donner généralement des adresses fictives explicites aux différents endroits a aidé.

Je suppose qu'en résumé, je dis que si vous voulez comprendre les pointeurs, vous devez comprendre les variables et ce qu'elles sont réellement dans les architectures modernes.


13
À mon humble avis, comprendre la pile et le tas sont aussi inutiles que les détails du processeur de bas niveau. La pile et le tas sont des détails d'implémentation. La spécification ISO C ne contient pas une seule mention du mot «pile» et K&R non plus.
sigjuice

4
@sigjuice: Vos objections passent à côté de la question et de la réponse. A) K&R C est un anachronisme B) ISO C n'est pas le seul langage avec des pointeurs, mes points 1 et 2 ont été développés contre un langage non basé sur C C) 95% des architectures (et non des langages) utilisent le tas / stack, il est assez courant que les exceptions soient expliquées par rapport à lui. D) Le point de la question était "pourquoi les gens ne comprennent-ils pas les pointeurs", pas "comment expliquer ISO C"
jkerian

9
@John Marchetti: Encore plus ... étant donné que la question était "Quel est le problème fondamental du problème des gens avec les pointeurs", je ne pense pas que les personnes qui posent les questions liées aux pointeurs seraient terriblement impressionnées par "Vous ne faites pas" t vraiment besoin de savoir "comme réponse. De toute évidence, ils ne sont pas d'accord. :)
jkerian

3
@jkerian Cela peut être obsolète, mais les trois ou quatre pages de K&R qui expliquent les pointeurs le font sans avoir besoin de détails d'implémentation. Une connaissance des détails de mise en œuvre est utile pour diverses raisons, mais à mon humble avis, cela ne devrait pas être une condition préalable à la compréhension des constructions clés d'un langage.
sigjuice

3
"Donner généralement des adresses fictives explicites aux différents endroits a aidé." -> +1
fredoverflow

146

Quand j'ai commencé à travailler avec eux, le plus gros problème que j'ai eu était la syntaxe.

int* ip;
int * ip;
int *ip;

sont tous les mêmes.

mais:

int* ip1, ip2;  //second one isn't a pointer!
int *ip1, *ip2;

Pourquoi? car la partie "pointeur" de la déclaration appartient à la variable et non au type.

Et puis déréférencer la chose utilise une notation très similaire:

*ip = 4;  //sets the value of the thing pointed to by ip to '4'
x = ip;   //hey, that's not '4'!
x = *ip;  //ahh... there's that '4'

Sauf quand vous avez réellement besoin d'un pointeur ... alors vous utilisez une esperluette!

int *ip = &x;

Hourra pour la cohérence!

Ensuite, apparemment juste pour être des imbéciles et prouver à quel point ils sont intelligents, de nombreux développeurs de bibliothèques utilisent des pointeurs vers des pointeurs vers des pointeurs, et s'ils s'attendent à un tableau de ces choses, pourquoi ne pas simplement passer un pointeur vers cela aussi .

void foo(****ipppArr);

pour appeler cela, j'ai besoin de l'adresse du tableau de pointeurs vers des pointeurs vers des pointeurs d'entiers:

foo(&(***ipppArr));

Dans six mois, lorsque je devrai maintenir ce code, je passerai plus de temps à essayer de comprendre ce que tout cela signifie que de réécrire à partir de zéro. (ouais, je me suis probablement trompé de syntaxe - ça fait un moment que je n'ai rien fait en C. ça me manque un peu, mais je suis un peu massochiste)


21
Votre commentaire sur le premier, >> * ip = 4; // définit la valeur de ip à '4' << est faux. Cela devrait être >> // définit la valeur de l'objet pointé par ip sur '4'
aaaa bbbb

8
Empiler trop de types les uns sur les autres est une mauvaise idée, quelle que soit la langue. Vous pouvez trouver l'écriture "foo (& (*** ipppArr));" étrange en C, mais en écrivant quelque chose comme "std :: map <std :: pair <int, int>, std :: pair <std :: vector <int>, std :: tuple <int, double, std :: list <int> >>> "en C ++ est également très complexe. Cela ne signifie pas que les pointeurs dans les conteneurs C ou STL en C ++ sont complexes. Cela signifie simplement que vous devez utiliser de meilleures définitions de type pour le rendre compréhensible pour le lecteur de votre code.
Patrick

20
Je ne peux sincèrement pas croire qu'un malentendu de la syntaxe soit la réponse la plus votée. C'est la partie la plus simple des pointeurs.
jason

4
Même en lisant cette réponse, j'ai été tenté d'obtenir une feuille de papier et de dessiner l'image. En C, je dessinais toujours des images.
Michael Easter

19
@Jason Quelle mesure objective de la difficulté existe-t-il autre que ce que la majorité des gens trouvent difficile?
Rupert Madden-Abbott

52

Une bonne compréhension des pointeurs nécessite des connaissances sur l'architecture de la machine sous-jacente.

Aujourd'hui, de nombreux programmeurs ne savent pas comment fonctionne leur machine, tout comme la plupart des gens qui savent conduire une voiture ne savent rien du moteur.


18
@dmckee: Eh bien, ai-je tort? Combien de programmeurs Java pourraient faire face à un segfault?
Robert Harvey

5
Les segfaults ont-ils un rapport avec le stick shift? - Un programmeur Java
Tom Anderson

6
@Robert: c'était un véritable complément. C'est un sujet difficile à discuter sans blesser les sentiments des gens. Et j'ai peur que mon commentaire ait précipité le conflit même que je pensais que vous aviez réussi à éviter. Mea cupla.
dmckee --- ex-moderator chaton

30
Je ne suis pas d'accord; vous n'avez pas besoin de comprendre l'architecture sous-jacente pour obtenir des pointeurs (ils sont de toute façon une abstraction).
jason

11
@Jason: En C, un pointeur est essentiellement une adresse mémoire. Travailler avec eux en toute sécurité est impossible sans comprendre l'architecture de la machine. Voir en.wikipedia.org/wiki/Pointer_(computing) et boredzo.org/pointers/#definition
Robert Harvey

42

Lorsqu'il s'agit de pointeurs, les personnes qui se confondent sont largement dans l'un des deux camps. J'ai été (suis?) Dans les deux.

La array[]foule

C'est la foule qui ne sait pas directement comment traduire la notation de pointeur en notation de tableau (ou ne sait même pas qu'elles sont même liées). Voici quatre façons d'accéder aux éléments d'un tableau:

  1. notation de tableau (indexation) avec le nom du tableau
  2. notation de tableau (indexation) avec le nom du pointeur
  3. notation du pointeur (le *) avec le nom du pointeur
  4. notation du pointeur (le *) avec le nom du tableau

 

int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;

array       element            pointer
notation    number     vals    notation

vals[0]     0          10      *(ptr + 0)
ptr[0]                         *(vals + 0)

vals[1]     1          20      *(ptr + 1)
ptr[1]                         *(vals + 1)

vals[2]     2          30      *(ptr + 2)
ptr[2]                         *(vals + 2)

vals[3]     3          40      *(ptr + 3)
ptr[3]                         *(vals + 3)

vals[4]     4          50      *(ptr + 4)
ptr[4]                         *(vals + 4)

L'idée ici est que l'accès aux tableaux via des pointeurs semble assez simple et direct, mais une tonne de choses très compliquées et intelligentes peuvent être faites de cette façon. Certains d'entre eux peuvent laisser les programmeurs C / C ++ expérimentés perplexes, sans parler des débutants inexpérimentés.

La foule reference to a pointeretpointer to a pointer

C'est un excellent article qui explique la différence et que je vais citer et voler du code :)

À titre d'exemple, il peut être très difficile de voir exactement ce que l'auteur voulait faire si vous tombiez sur quelque chose comme ceci:

//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(pvar);
  ....
  return 0;
}

Ou, dans une moindre mesure, quelque chose comme ceci:

//function prototype
void func(int** ppInt);

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(&pvar);
  ....
  return 0;
}

Alors en fin de compte, que résolvons-nous vraiment avec tout ce charabia? Rien.

Nous avons maintenant vu la syntaxe de ptr-to-ptr et ref-to-ptr. Y a-t-il des avantages de l'un par rapport à l'autre? J'ai peur, non. L'utilisation de l'un des deux pour certains programmeurs n'est que des préférences personnelles. Certains qui utilisent ref-to-ptr disent que la syntaxe est "plus propre" tandis que certains qui utilisent ptr-to-ptr, disent que la syntaxe ptr-to-ptr rend plus clair pour ceux qui lisent ce que vous faites.

Cette complexité et l' interchangeabilité apparente ( apparente audacieuse) avec les références, qui est souvent une autre mise en garde des pointeurs et une erreur des nouveaux venus, rend la compréhension des pointeurs difficile. Il est également important de comprendre, à cause de la fin, que les pointeurs de références sont illégales en C et C ++ pour la confusion des raisons qui vous prennent en lvalue- rvaluesémantique.

Comme une réponse précédente l'a fait remarquer, vous n'aurez souvent que ces programmeurs hotshot qui pensent qu'ils sont intelligents en utilisant ******awesome_var->lol_im_so_clever()et la plupart d'entre nous sont probablement coupables d'écrire de telles atrocités parfois, mais ce n'est tout simplement pas un bon code, et ce n'est certainement pas maintenable. .

Eh bien, cette réponse s'est avérée être plus longue que ce que j'avais espéré ...


5
Je pense que vous avez peut-être donné une réponse C ++ à une question C ici ... au moins la deuxième partie.
detly

Les pointeurs vers les pointeurs s'appliquent également à C: p
David Titarenco

1
Il h? Je ne vois que des pointeurs vers des pointeurs lors du passage de tableaux - votre deuxième exemple n'est pas vraiment applicable à la plupart des codes C décents. De plus, vous faites glisser C dans le désordre de C ++ - les références en C n'existent pas.
nouveau123456

Vous devez traiter des pointeurs vers des pointeurs dans de nombreux cas légitimes. Par exemple, lorsqu'il s'agit d'un pointeur de fonction pointant sur une fonction qui renvoie un pointeur. Autre exemple: une structure qui peut contenir un nombre variable d'autres structures. Et bien d'autres ...
David Titarenco

2
"les pointeurs vers des références sont illégaux en C" - plus comme "absent" :)
Kos

29

Je blâme la qualité des matériaux de référence et les personnes qui enseignent personnellement; la plupart des concepts en C (mais en particulier les pointeurs) sont tout simplement mal enseignés . Je continue de menacer d'écrire mon propre livre C (intitulé La dernière chose dont le monde a besoin est un autre livre sur le langage de programmation C ), mais je n'ai ni le temps ni la patience pour le faire. Alors je traîne ici et jette des citations aléatoires du Standard aux gens.

Il y a aussi le fait que lorsque C a été initialement conçu, on supposait que vous compreniez l'architecture de la machine à un niveau assez détaillé simplement parce qu'il n'y avait aucun moyen de l'éviter dans votre travail quotidien (la mémoire était si serrée et les processeurs étaient si lents il fallait comprendre comment ce que vous écriviez affectait les performances).


3
Oui. 'int toto = 5; int * pfoo = & foo; Vous voyez à quel point c'est utile? OK, je continue ... «Je n'ai pas vraiment utilisé de pointeurs avant d'écrire ma propre bibliothèque de listes à double lien.
John Lopez

2
+1. J'utilise pour enseigner aux étudiants CS100 et tant de leurs problèmes ont été résolus simplement en passant en revue les pointeurs d'une manière compréhensible.
benzado

1
+1 pour le contexte historique. Ayant commencé longtemps après cette période, cela ne m'est jamais venu à l'esprit.
Lumi

26

Il y a un excellent article soutenant l'idée que les pointeurs sont difficiles sur le site de Joel Spolsky - The Perils of JavaSchools .

[Clause de non-responsabilité - Je ne suis pas un haineux de Java en soi .]


2
@Jason - c'est vrai mais cela n'annule pas l'argument.
Steve Townsend

4
Spolsky ne dit pas que JavaSchools est la raison pour laquelle les gens trouvent les pointeurs difficiles. Il dit qu'il en résulte des personnes analphabètes avec des pointeurs avec des diplômes en informatique.
benzado

1
@benzado - bon point - mon bref article serait amélioré s'il lisait «un excellent article soutenant la notion que les pointeurs sont durs». Ce que l'article implique, c'est que «avoir un diplôme CS d'une« bonne école »» n'est pas un aussi bon prédicteur de succès qu'un développeur qu'il l'était auparavant, alors que «comprend les pointeurs» (et la récursivité) l'est toujours.
Steve Townsend

1
@Steve Townsend: Je pense que vous passez à côté de l'argument de M. Spolsky.
jason

2
@Steve Townsend: M. Spolsky soutient que les écoles Java élèvent une génération de programmeurs qui ne connaissent pas les pointeurs et la récursivité, non pas que les pointeurs sont difficiles à cause de la prévalence des écoles Java. Comme vous l'avez dit "il y a un excellent article sur les raisons pour lesquelles c'est difficile" et lié audit article, il semble que vous ayez cette dernière interprétation. Pardonne-moi si je me trompe.
jason

24

La plupart des choses sont plus difficiles à comprendre si vous n'êtes pas ancré dans la connaissance qui est «en dessous». Quand j'ai enseigné CS, cela est devenu beaucoup plus facile lorsque j'ai commencé mes étudiants à programmer une "machine" très simple, un ordinateur décimal simulé avec des opcodes décimaux dont la mémoire se composait de registres décimaux et d'adresses décimales. Ils mettraient en place des programmes très courts pour, par exemple, ajouter une série de chiffres pour obtenir un total. Ensuite, ils le faisaient un pas pour regarder ce qui se passait. Ils pouvaient maintenir la touche «entrée» enfoncée et la regarder tourner «vite».

Je suis sûr que presque tout le monde sur SO se demande pourquoi il est utile d'être aussi basique. On oublie ce que c'était de ne pas savoir programmer. Jouer avec un tel ordinateur jouet met en place des concepts sans lesquels vous ne pouvez pas programmer, tels que l'idée que le calcul est un processus étape par étape, en utilisant un petit nombre de primitives de base pour construire des programmes et le concept de mémoire variables comme des endroits où les nombres sont stockés, dans lesquels l'adresse ou le nom de la variable est distinct du nombre qu'elle contient. Il existe une distinction entre l'heure à laquelle vous entrez dans le programme et l'heure à laquelle il "s'exécute". J'assimile l'apprentissage de la programmation à traverser une série de "ralentisseurs", tels que des programmes très simples, puis des boucles et des sous-programmes, puis des tableaux, puis des E / S séquentielles, puis des pointeurs et une structure de données.

Enfin, en arrivant à C, les pointeurs sont déroutants bien que K&R ait fait un très bon travail pour les expliquer. La façon dont je les ai appris en C était de savoir les lire - de droite à gauche. Comme quand je vois int *pdans ma tête je dis " ppointe vers un int". C a été inventé comme un pas en avant du langage d'assemblage et c'est ce que j'aime à ce sujet - il est proche de ce «terrain». Les pointeurs, comme toute autre chose, sont plus difficiles à comprendre si vous ne disposez pas de ces bases.


1
Une bonne façon d'apprendre cela est de programmer des microcontrôleurs 8 bits. Ils sont faciles à comprendre. Prenez les contrôleurs Atmel AVR; ils sont même pris en charge par gcc.
Xenu

@Xenu: Je suis d'accord. Pour moi, c'était Intel 8008 et 8051 :-)
Mike Dunlavey

Pour moi, c'était un ordinateur 8 bits personnalisé (le «Peut-être») au MIT dans la pénombre du temps.
QuantumMechanic

Mike - vous devriez obtenir un CARDIAQUE à vos étudiants :)
Mécanicien quantique

1
@Quantum: CARDIAQUE - bon, n'en avait pas entendu parler. Le "Peut-être" - laissez-moi deviner, était-ce lorsque Sussman (et al) avait des gens qui lisaient le livre de Mead-Conway et fabriquaient leurs propres puces LSI? C'était un peu après mon séjour là-bas.
Mike Dunlavey

17

Je n'ai pas eu de pointeurs avant d'avoir lu la description dans K&R. Jusque-là, les pointeurs n'avaient pas de sens. J'ai lu tout un tas de trucs où les gens disaient "N'apprends pas les pointeurs, ils sont déroutants et vont te faire mal à la tête et te donner des anévrismes" alors j'ai évité ça pendant longtemps, et j'ai créé cet air inutile de concept difficile .

Sinon, surtout ce que je pensais était, pourquoi diable voudriez-vous une variable dont vous devez passer par des cerceaux pour obtenir la valeur de, et si vous vouliez lui attribuer des choses, vous deviez faire des choses étranges pour obtenir des valeurs. en eux. L'intérêt d'une variable est de stocker une valeur, pensai-je, alors pourquoi quelqu'un voulait la compliquer me dépassait. "Donc, avec un pointeur, vous devez utiliser l' *opérateur pour obtenir sa valeur ??? Quel genre de variable loufoque est-ce?" , J'ai pensé. Inutile, sans jeu de mots.

La raison pour laquelle c'était compliqué était que je ne comprenais pas qu'un pointeur était une adresse vers quelque chose. Si vous expliquez que c'est une adresse, que c'est quelque chose qui contient une adresse à autre chose, et que vous pouvez manipuler cette adresse pour faire des choses utiles, je pense que cela pourrait dissiper la confusion.

Une classe qui nécessitait d'utiliser des pointeurs pour accéder / modifier les ports sur un PC, en utilisant l'arithmétique des pointeurs pour adresser différents emplacements de mémoire, et en examinant un code C plus compliqué qui modifiait leurs arguments, m'a désabusé de l'idée que les pointeurs étaient, eh bien, inutiles.


4
Si vous disposez de ressources limitées pour travailler (RAM, ROM, CPU), comme dans les applications embarquées, les pointeurs ont rapidement beaucoup plus de sens.
Nick T du

+1 pour le commentaire de Nick - en particulier pour passer des structures.
nouveau123456

12

Voici un exemple de pointeur / tableau qui m'a donné une pause. Supposons que vous ayez deux tableaux:

uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];

Et votre objectif est de copier le contenu uint8_t de la destination source en utilisant memcpy (). Devinez lequel des éléments suivants atteint cet objectif:

memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));

La réponse (Spoiler Alert!) Est TOUTES. "destination", "& destination" et "& destination [0]" ont tous la même valeur. "& destination" est un type différent des deux autres, mais il s'agit toujours de la même valeur. Il en va de même pour les permutations de «source».

En passant, je préfère personnellement la première version.


Je préfère aussi la première version (moins de ponctuation).
sigjuice du

++ Moi aussi, mais vous devez vraiment faire attention sizeof(source), car si sourcec'est un pointeur, sizeofce ne sera pas ce que vous voulez. J'écris parfois (pas toujours) sizeof(source[0]) * number_of_elements_of_sourcejuste pour rester loin de ce bug.
Mike Dunlavey

destination, & destination, & destination [0] ne sont pas du tout les mêmes - mais chacun sera converti par un mécanisme différent en le même vide * lorsqu'il est utilisé dans memcpy. Cependant, lorsqu'il est utilisé comme argument de sizeof, vous obtiendrez deux résultats différents et trois résultats différents sont possibles.
gnasher729

Je pensais que l'adresse de l'opérateur était requise?
MarcusJ

7

Je devrais commencer par dire que C et C ++ ont été les premiers langages de programmation que j'ai appris. J'ai commencé avec le C, puis beaucoup fait du C ++ à l'école, puis je suis retourné au C pour le maîtriser.

La première chose qui m'a dérouté à propos des pointeurs lors de l'apprentissage de C était la simple:

char ch;
char str[100];
scanf("%c %s", &ch, str);

Cette confusion était principalement enracinée dans le fait d'avoir été introduit à l'utilisation de la référence à une variable pour les arguments OUT avant que les pointeurs ne me soient correctement présentés. Je me souviens que j'ai sauté les premiers exemples en C pour les nuls parce qu'ils étaient trop simples pour ne jamais faire fonctionner le premier programme que j'ai écrit (probablement à cause de cela).

Ce qui était déroutant à ce sujet, c'était ce &chque signifiait réellement et pourquoi strn'en avait pas besoin.

Après m'être familiarisé avec cela, je me souviens avoir été confus au sujet de l'allocation dynamique. J'ai réalisé à un moment donné qu'avoir des pointeurs vers des données n'était pas extrêmement utile sans allocation dynamique d'un certain type, alors j'ai écrit quelque chose comme:

char * x = NULL;
if (y) {
     char z[100];
     x = z;
}

pour essayer d'allouer dynamiquement de l'espace. Ça n'a pas marché. Je n'étais pas sûr que cela fonctionnerait, mais je ne savais pas comment cela pourrait fonctionner autrement.

J'ai appris plus tard sur mallocet new, mais ils me semblaient vraiment des générateurs de mémoire magique. Je ne savais rien de leur fonctionnement.

Quelque temps plus tard, on m'enseignait à nouveau la récursivité (je l'avais appris par moi-même auparavant, mais j'étais en classe maintenant) et j'ai demandé comment cela fonctionnait sous le capot - où étaient stockées les variables séparées. Mon professeur a dit "sur la pile" et beaucoup de choses sont devenues claires pour moi. J'avais déjà entendu le terme et j'avais implémenté des piles de logiciels auparavant. J'avais entendu d'autres parler de "la pile" bien avant, mais je l'avais oublié.

À cette époque, j'ai également réalisé que l'utilisation de tableaux multidimensionnels en C pouvait être très déroutante. Je savais comment ils fonctionnaient, mais ils étaient tellement faciles à comprendre que j'ai décidé d'essayer de les utiliser chaque fois que je le pouvais. Je pense que le problème ici était principalement syntaxique (en particulier leur passage ou leur retour à partir de fonctions).

Depuis que j'écrivais du C ++ pour l'école pour la prochaine année ou deux, j'ai acquis beaucoup d'expérience en utilisant des pointeurs pour les structures de données. Ici, j'ai eu une nouvelle série de problèmes - mélanger les pointeurs. J'aurais plusieurs niveaux de pointeurs (des choses comme node ***ptr;) me trébucher. Je déréférerais un pointeur le mauvais nombre de fois et j'aurais finalement recours à la détermination du nombre dont *j'avais besoin par essais et erreurs.

À un moment donné, j'ai appris comment fonctionnait le tas d'un programme (en quelque sorte, mais assez bien pour qu'il ne me tenait plus éveillé la nuit). Je me souviens avoir lu que si vous regardez quelques octets avant le mallocretour du pointeur sur un certain système, vous pouvez voir la quantité de données réellement allouée. J'ai réalisé que le code mallocpourrait demander plus de mémoire du système d'exploitation et que cette mémoire ne faisait pas partie de mes fichiers exécutables. Avoir une idée de travail décente de la façon dont mallocfonctionne est vraiment utile.

Peu de temps après, j'ai suivi un cours d'assemblage, qui ne m'a pas appris autant sur les pointeurs que la plupart des programmeurs le pensent probablement. Cela m'a amené à réfléchir davantage à l'assembly dans lequel mon code pourrait être traduit. J'avais toujours essayé d'écrire du code efficace, mais maintenant j'avais une meilleure idée de la façon de le faire.

J'ai également suivi quelques cours où j'ai dû écrire des lisp . Lors de l'écriture de lisp, je n'étais pas aussi préoccupé par l'efficacité que je l'étais en C.J'avais très peu d'idée en quoi ce code pourrait être traduit s'il était compilé, mais je savais qu'il semblait utiliser beaucoup de symboles nommés locaux (variables). les choses sont beaucoup plus faciles. À un moment donné, j'ai écrit du code de rotation d'arborescence AVL dans un peu de lisp, que j'ai eu du mal à écrire en C ++ à cause de problèmes de pointeur. J'ai réalisé que mon aversion pour ce que je pensais être des variables locales excessives avait entravé ma capacité à écrire cela et plusieurs autres programmes en C ++.

J'ai également suivi un cours de compilateurs. Pendant que dans ce cours, je suis passé au matériel avancé et j'ai appris à propos de l' assignation unique statique (SSA) et des variables mortes, ce qui n'est pas si important que cela, sauf que cela m'a appris que tout compilateur décent fera un travail décent en traitant des variables qui sont plus utilisé. Je savais déjà que plus de variables (y compris des pointeurs) avec des types corrects et de bons noms m'aideraient à garder les choses en tête, mais maintenant je savais aussi que les éviter pour des raisons d'efficacité était encore plus stupide que mes professeurs moins soucieux de la micro-optimisation ne le disaient. moi.

Donc pour moi, connaître un peu la disposition de la mémoire d'un programme a beaucoup aidé. Penser à ce que signifie mon code, à la fois symboliquement et sur le matériel, m'aide. L'utilisation de pointeurs locaux qui ont le bon type aide beaucoup. J'écris souvent du code qui ressemble à:

int foo(struct frog * f, int x, int y) {
    struct leg * g = f->left_leg;
    struct toe * t = g->big_toe;
    process(t);

de sorte que si je bousille un type de pointeur, l'erreur du compilateur indique clairement quel est le problème. Si j'ai fait:

int foo(struct frog * f, int x, int y) {
    process(f->left_leg->big_toe);

et avoir un type de pointeur erroné là-dedans, l'erreur du compilateur serait beaucoup plus difficile à comprendre. Je serais tenté de recourir à des changements par essais et erreurs dans ma frustration, et probablement aggraver les choses.


1
+1. Complet et perspicace. J'avais oublié scanf, mais maintenant que vous en parlez, je me souviens avoir la même confusion.
Joe White

6

Avec le recul, il y avait quatre choses qui m'ont vraiment aidé à comprendre enfin les pointeurs. Avant cela, je pouvais les utiliser, mais je ne les comprenais pas complètement. Autrement dit, je savais que si je suivais les formulaires, j'obtiendrais les résultats que je souhaitais, mais je ne comprenais pas pleinement le «pourquoi» des formulaires. Je comprends que ce n'est pas exactement ce que vous avez demandé, mais je pense que c'est un corollaire utile.

  1. Écriture d'une routine qui a pris un pointeur sur un entier et modifié l'entier. Cela m'a donné les formes nécessaires sur lesquelles construire des modèles mentaux du fonctionnement des pointeurs.

  2. Allocation de mémoire dynamique unidimensionnelle. Comprendre l'allocation de mémoire 1-D m'a fait comprendre le concept du pointeur.

  3. Allocation de mémoire dynamique bidimensionnelle. Déterminer l'allocation de mémoire 2D a renforcé ce concept, mais m'a également appris que le pointeur lui-même nécessite du stockage et doit être pris en compte.

  4. Différences entre les variables de pile, les variables globales et la mémoire de tas. Comprendre ces différences m'a appris les types de mémoire vers lesquels les pointeurs pointent / se réfèrent.

Chacun de ces éléments nécessitait d'imaginer ce qui se passait à un niveau inférieur - construire un modèle mental qui satisfaisait tous les cas que je pouvais penser à lui lancer. Cela a pris du temps et des efforts, mais cela en valait la peine. Je suis convaincu que pour comprendre les pointeurs, il faut construire ce modèle mental sur la façon dont ils fonctionnent et comment ils sont mis en œuvre.

Revenons maintenant à votre question initiale. Sur la base de la liste précédente, il y avait plusieurs éléments que j'avais du mal à saisir à l'origine.

  1. Comment et pourquoi utiliserait-on un pointeur.
  2. Comment sont-ils différents et pourtant similaires aux tableaux.
  3. Comprendre où les informations du pointeur sont stockées.
  4. Comprendre sur quoi et où il pointe le pointeur.

Hé, pourriez-vous m'indiquer un article / livre / votre dessin / doodle / quelque chose où je pourrais apprendre de la même manière que vous avez décrit dans votre réponse? Je crois fermement que c'est la voie à suivre pour bien apprendre, essentiellement n'importe quoi. Compréhension profonde et bons modèles mentaux
Alexander Starbuck

1
@AlexStarbuck - Je ne veux pas que cela semble désinvolte, mais la méthode scientifique est un excellent outil. Dessinez-vous une image de ce que vous pensez qu'il pourrait se passer pour un scénario particulier. Programmez quelque chose pour le tester et analysez ce que vous avez. Cela correspond-il à ce que vous attendez? Si non, identifiez les différences? Répétez si nécessaire, en augmentant progressivement la complexité pour tester à la fois votre compréhension et vos modèles mentaux.
Sparky

6

J'ai fait travailler mon "moment de pointeur" sur certains programmes de téléphonie en C. J'ai dû écrire un émulateur d'échange AXE10 en utilisant un analyseur de protocole qui ne comprenait que le C. classique. Tout reposait sur la connaissance des pointeurs. J'ai essayé d'écrire mon code sans eux (hé, j'étais "pré-pointer" m'a coupé un peu de mou) et j'ai échoué complètement.

La clé pour les comprendre, pour moi, était l'opérateur & (adresse). Une fois que j'ai compris que cela &isignifiait «l'adresse de i», alors comprendre que cela *isignifiait «le contenu de l'adresse pointée par i» est venu un peu plus tard. Chaque fois que j'écrivais ou lisais mon code, je répétais toujours ce que «&» signifiait et ce que «*» signifiait et finalement j'en suis venu à les utiliser intuitivement.

À ma honte, j'ai été forcé de passer en VB puis en Java, donc ma connaissance du pointeur n'est pas aussi précise qu'elle l'était autrefois, mais je suis content d'être "post-pointeur". Ne me demandez pas d'utiliser une bibliothèque qui m'oblige à comprendre * * p, cependant.


Si &iest l'adresse et *ile contenu, qu'est-ce que c'est i?
Thomas Ahle

2
Je surcharge en quelque sorte l'utilisation de i. Pour une variable arbitraire i, & i signifie "l'adresse de" i, i à elle seule signifie "le contenu de & i", et * i signifie "traiter le contenu de & i comme une adresse, aller à cette adresse et renvoyer le Contenu".
Gary Rowe

5

La principale difficulté avec les pointeurs, du moins pour moi, est que je n'ai pas commencé avec C. J'ai commencé avec Java. Toute la notion de pointeurs était vraiment étrangère jusqu'à quelques cours à l'université où je devais connaître C. Alors je me suis appris les bases mêmes du C et comment utiliser les pointeurs dans leur sens très basique. Même dans ce cas, chaque fois que je lis du code C, je dois rechercher la syntaxe du pointeur.

Donc, dans mon expérience très limitée (1 an dans le monde réel + 4 à l'université), les pointeurs me déroutent car je n'ai jamais eu à vraiment l'utiliser dans autre chose qu'une salle de classe. Et je peux sympathiser avec les étudiants qui commencent maintenant CS avec JAVA au lieu de C ou C ++. Comme vous l'avez dit, vous avez appris les pointeurs à l'ère «néolithique» et vous les utilisez probablement depuis lors. Pour nous, les nouveaux venus, la notion d'allocation de mémoire et d'arithmétique des pointeurs est vraiment étrangère parce que tous ces langages l'ont abstraite.

PS Après avoir lu l'essai de Spolsky, sa description de «JavaSchools» ne ressemblait en rien à ce que j'ai vécu à l'université de Cornell ('05 -'09). J'ai pris les structures et la programmation fonctionnelle (sml), les systèmes d'exploitation (C), les algorithmes (stylo et papier) et tout un tas d'autres cours qui n'étaient pas enseignés en java. Cependant, toutes les classes d'introduction et les cours facultatifs ont tous été réalisés en java car il est utile de ne pas réinventer la roue lorsque vous essayez de faire quelque chose de plus haut niveau que l'implémentation d'une table de hachage avec des pointeurs.


4
Honnêtement, étant donné que vous avez encore des difficultés avec les pointeurs, je ne suis pas sûr que votre expérience à Cornell contredit substantiellement l'article de Joel. De toute évidence, votre cerveau est suffisamment connecté à un état d'esprit Java pour faire valoir son point de vue.
jkerian

5
Wat? Les références en Java (ou C #, ou Python, ou probablement des dizaines d'autres langages) ne sont que des pointeurs sans arithmétique. Comprendre les pointeurs signifie comprendre pourquoi void foo(Clazz obj) { obj = new Clazz(); }un no-op alors que void bar(Clazz obj) { obj.quux = new Quux(); }mute l'argument ...

1
Je sais quelles sont les références en Java, mais je dis simplement que si vous me demandez de faire de la réflexion en Java ou d'écrire quelque chose de significatif en CI, vous ne pouvez pas simplement l'évoquer. Cela nécessite beaucoup de recherche, comme l'apprendre pour la première fois, à chaque fois.
boîte à chaussures639

1
Comment se fait-il que vous ayez traversé une classe de systèmes d'exploitation en C sans maîtriser le C? Aucune infraction prévue, c'est juste que je me souviens avoir à peu près développer un système d'exploitation simple à partir de zéro. J'ai dû utiliser des pointeurs mille fois ...
Gravity

5

Voici une non-réponse: utilisez cdecl (ou c ++ decl) pour le comprendre:

eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int

4

Ils ajoutent une dimension supplémentaire au code sans modification significative de la syntaxe. Penses-y:

int a;
a = 5

Il n'y a qu'une seule chose à changer: a. Vous pouvez écrire a = 6et les résultats sont évidents pour la plupart des gens. Mais maintenant, considérez:

int *a;
a = &some_int;

Il y a deux choses aqui sont pertinentes à des moments différents: la valeur réelle de a, le pointeur et la valeur «derrière» le pointeur. Vous pouvez changer a:

a = &some_other_int;

... et some_intest toujours quelque part avec la même valeur. Mais vous pouvez également changer la chose vers laquelle il pointe:

*a = 6;

Il y a un fossé conceptuel entre a = 6, qui n'a que des effets secondaires locaux, et *a = 6qui pourrait affecter un tas d'autres choses ailleurs. Ce que je veux dire ici n'est pas que le concept d'indirection soit intrinsèquement délicat, mais que parce que vous pouvez faire à la fois la chose immédiate, locale aou indirecte avec *a... c'est peut-être ce qui déroute les gens.


4

J'avais programmé en C ++ pendant environ 2 ans, puis je me suis converti en Java (5 ans) et je n'ai jamais regardé en arrière. Cependant, quand j'ai récemment dû utiliser des trucs natifs, j'ai découvert (avec stupéfaction) que je n'avais rien oublié des pointeurs et je les trouve même faciles à utiliser. C'est un contraste frappant avec ce que j'ai vécu il y a 7 ans lorsque j'ai essayé de saisir le concept pour la première fois. Donc, je suppose que comprendre et aimer est une question de maturité de la programmation? :)

OU

Les pointeurs sont comme faire du vélo, une fois que vous avez compris comment travailler avec eux, il ne faut pas l'oublier.

Dans l'ensemble, difficile à comprendre ou non, l'idée du pointeur dans son ensemble est TRÈS éducative et je pense qu'elle devrait être comprise par chaque programmeur, qu'il programme sur un langage avec des pointeurs ou non.


3

Les pointeurs sont difficiles à cause de l'indirection.


"On dit qu'il n'y a pas de problème en informatique qui ne puisse être résolu par un autre niveau d'indirection" (aucune idée de qui l'a dit en premier, cependant)
L'archétype Paul

C'est comme de la magie, où la mauvaise direction est ce qui déroute les gens (mais c'est totalement génial)
Nick T

3

Les pointeurs (ainsi que certains autres aspects du travail de bas niveau) obligent l'utilisateur à supprimer la magie.

La plupart des programmeurs de haut niveau aiment la magie.


3

Les pointeurs sont un moyen de gérer la différence entre une poignée vers un objet et un objet lui-même. (ok, pas nécessairement des objets, mais vous savez ce que je veux dire, ainsi que où est mon esprit)

À un moment donné, vous devrez probablement faire face à la différence entre les deux. Dans un langage moderne de haut niveau, cela devient la distinction entre copie par valeur et copie par référence. Quoi qu'il en soit, c'est un concept souvent difficile à saisir pour les programmeurs.

Cependant, comme cela a été souligné, la syntaxe pour gérer ce problème en C est laide, incohérente et déroutante. Finalement, si vous essayez vraiment de le comprendre, un pointeur aura du sens. Mais quand vous commencez à traiter des pointeurs vers des pointeurs, et ainsi de suite ad nauseum, cela devient vraiment déroutant pour moi ainsi que pour d'autres personnes.

Une autre chose importante à retenir à propos des pointeurs est qu'ils sont dangereux. C est le langage d'un maître programmeur. Cela suppose que vous savez ce que vous faites et vous donne ainsi le pouvoir de vraiment gâcher les choses. Alors que certains types de programmes doivent encore être écrits en C, la plupart des programmes ne le font pas, et si vous avez un langage qui fournit une meilleure abstraction pour la différence entre un objet et son handle, alors je vous suggère de l'utiliser.

En effet, dans de nombreuses applications C ++ modernes, il arrive souvent que toute arithmétique de pointeur requise soit encapsulée et abstraite. Nous ne voulons pas que les développeurs fassent de l'arithmétique des pointeurs partout. Nous voulons une API centralisée et bien testée qui effectue l'arithmétique des pointeurs au niveau le plus bas. Les modifications apportées à ce code doivent être effectuées avec beaucoup de soin et des tests approfondis.


3

Je pense qu'une des raisons pour lesquelles les pointeurs C sont difficiles est qu'ils combinent plusieurs concepts qui ne sont pas vraiment équivalents; pourtant, comme ils sont tous implémentés à l'aide de pointeurs, les gens peuvent avoir du mal à démêler les concepts.

En C, les pointeurs sont utilisés, entre autres:

  • Définir des structures de données récursives

En C, vous définiriez une liste liée d'entiers comme ceci:

struct node {
  int value;
  struct node* next;
}

Le pointeur n'est là que parce que c'est la seule façon de définir une structure de données récursive en C, lorsque le concept n'a vraiment rien à voir avec un détail de bas niveau comme les adresses mémoire. Considérez l'équivalent suivant dans Haskell, qui ne nécessite pas l'utilisation de pointeurs:

data List = List Int List | Null

Assez simple - une liste est soit vide, soit formée à partir d'une valeur et du reste de la liste.

  • Itérer sur des chaînes et des tableaux

Voici comment appliquer une fonction fooà chaque caractère d'une chaîne en C:

char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }

Bien qu'il utilise également un pointeur comme itérateur, cet exemple a très peu de points communs avec le précédent. La création d'un itérateur que vous pouvez incrémenter est un concept différent de la définition d'une structure de données récursive. Aucun de ces deux concepts n'est particulièrement lié à l'idée d'une adresse mémoire.

  • Atteindre le polymorphisme

Voici une véritable signature de fonction trouvée dans glib :

typedef struct g_list GList;

void  g_list_foreach    (GList *list,
                 void (*func)(void *data, void *user_data),
                         void* user_data);

Whoa! C'est pas mal de void*'s. Et c'est juste pour déclarer une fonction qui itère sur une liste qui peut contenir n'importe quel type de chose, en appliquant une fonction à chaque membre. Comparez-le à la façon dont il mapest déclaré dans Haskell:

map::(a->b)->[a]->[b]

C'est beaucoup plus simple: mapc'est une fonction qui prend une fonction qui convertit un aen a b, et l'applique à une liste de a's pour produire une liste de b' s. Tout comme dans la fonction C g_list_foreach, mapn'a pas besoin de savoir quoi que ce soit dans sa propre définition sur les types auxquels elle sera appliquée.

Pour résumer:

Je pense que les pointeurs C seraient beaucoup moins déroutants si les gens apprenaient d'abord les structures de données récursives, les itérateurs, le polymorphisme, etc. en tant que concepts séparés, puis apprenaient comment les pointeurs peuvent être utilisés pour implémenter ces idées en C , plutôt que de les écraser. concepts réunis en un seul sujet de "pointeurs".


Utilisation abusive de c != NULLvotre exemple "Hello world" ... vous voulez dire *c != '\0'.
Olaf Seibert

2

Je pense que cela nécessite une base solide, probablement au niveau de la machine, avec une introduction au code machine, à l'assemblage et à la façon de représenter les éléments et la structure de données dans la RAM. Cela prend un peu de temps, des devoirs ou des exercices de résolution de problèmes et une réflexion.

Mais si une personne connaît des langages de haut niveau au début (ce qui n'est rien de mal - un charpentier utilise une hache. Une personne qui a besoin de diviser un atome utilise autre chose. Nous avons besoin de gens qui sont charpentiers, et nous avons des gens qui étudient les atomes) et cette personne qui connaît un langage de haut niveau reçoit une introduction de 2 minutes aux pointeurs, puis il est difficile de s'attendre à ce qu'elle comprenne l'arithmétique des pointeurs, les pointeurs vers les pointeurs, le tableau de pointeurs vers les chaînes de taille variable, et le tableau de tableau de caractères, etc. Une base solide de bas niveau peut beaucoup aider.


2
Les pointeurs Groking ne nécessitent pas de compréhension du code machine ou de l'assemblage.
jason

Exiger, non. Mais les gens qui comprennent l'assemblage trouveraient probablement des pointeurs beaucoup, beaucoup plus facilement, car ils ont déjà fait la plupart (sinon la totalité) des connexions mentales nécessaires.
cHao

2

Le problème que j'ai toujours eu (principalement autodidacte) est le "quand" utiliser un pointeur. Je peux comprendre la syntaxe pour construire un pointeur mais j'ai besoin de savoir dans quelles circonstances un pointeur doit être utilisé.

Suis-je le seul à avoir cet état d'esprit? ;-)


Je comprends ça. Ma réponse traite un peu de cela.
J.Polfer

2

Il était une fois ... Nous avions des microprocesseurs 8 bits et tout le monde écrivait en montage. La plupart des processeurs incluaient un certain type d'adressage indirect utilisé pour les tables de sauts et les noyaux. Lorsque des langages de niveau supérieur sont apparus, nous avons ajouté une fine couche d'abstraction et les avons appelés pointeurs. Au fil des ans, nous nous sommes de plus en plus éloignés du matériel. Ce n'est pas nécessairement une mauvaise chose. Ils sont appelés langues de niveau supérieur pour une raison. Plus je peux me concentrer sur ce que je veux faire plutôt que sur les détails de la façon dont cela est fait, mieux c'est.


2

Il semble que de nombreux étudiants ont un problème avec le concept d'indirection, en particulier lorsqu'ils rencontrent le concept d'indirection pour la première fois. Je me souviens de l'époque où j'étais étudiant que sur les +100 étudiants de mon cours, seule une poignée de personnes comprenaient vraiment les pointeurs.

Le concept d'indirection n'est pas quelque chose que nous utilisons souvent dans la vie réelle, c'est donc un concept difficile à saisir au départ.


2

Récemment, je viens d'avoir le moment du clic du pointeur et j'ai été surpris de le trouver déroutant. C'était plus que tout le monde en parlait tellement, que je supposais qu'il y avait de la magie noire.

Voici comment je l'ai eue. Imaginez que toutes les variables définies disposent d'un espace mémoire au moment de la compilation (sur la pile). Si vous voulez un programme capable de gérer des fichiers de données volumineux tels que de l'audio ou des images, vous ne voudriez pas d'une quantité fixe de mémoire pour ces structures potentielles. Vous attendez donc à l'exécution pour affecter une certaine quantité de mémoire à la conservation de ces données (sur le tas).

Une fois que vous avez vos données en mémoire, vous ne voulez pas copier ces données tout autour de votre bus mémoire chaque fois que vous souhaitez exécuter une opération dessus. Supposons que vous souhaitiez appliquer un filtre à vos données d'image. Vous avez un pointeur qui commence au début des données que vous avez affectées à l'image, et une fonction s'exécute sur ces données, les modifiant en place. Si vous ne saviez pas ce que vous faites, vous finiriez probablement par faire des doublons de données, au fur et à mesure que vous les exécutiez.

Au moins c'est comme ça que je le vois en ce moment!


Après réflexion, vous pouvez définir une quantité fixe de mémoire pour contenir des images / audio / vidéo, par exemple sur un appareil à mémoire limitée, mais vous devrez alors gérer une sorte de solution de streaming dans et hors de la mémoire.
Chris Barry du

1

Parlant en tant que débutant C ++ ici:

Le système de pointeur a mis un certain temps à digérer, pas nécessairement à cause du concept mais à cause de la syntaxe C ++ relative à Java. Voici quelques éléments que j'ai trouvés déroutants:

(1) Déclaration de variable:

A a(1);

contre.

A a = A(1);

contre.

A* a = new A(1); 

et apparemment

A a(); 

est une déclaration de fonction et non une déclaration de variable. Dans d'autres langages, il n'y a fondamentalement qu'une seule façon de déclarer une variable.

(2) L'esperluette est utilisée de différentes manières. Si c'est

int* i = &a;

alors le & a est une adresse mémoire.

OTOH, si c'est

void f(int &a) {}

alors le & a est un paramètre passé par référence.

Bien que cela puisse sembler anodin, cela peut être déroutant pour les nouveaux utilisateurs - je viens de Java et Java est un langage avec une utilisation plus uniforme des opérateurs

(3) Relation tableau-pointeur

Une chose qui est un peu frustrante à comprendre est qu'un pointeur

int* i

peut être un pointeur vers un int

int *i = &n; // 

ou

peut être un tableau vers un entier

int* i = new int[5];

Et puis, juste pour rendre les choses plus compliquées, les pointeurs et les tableaux ne sont pas interchangeables dans tous les cas et les pointeurs ne peuvent pas être passés en tant que paramètres de tableau.

Cela résume certaines des frustrations de base que j'ai eues avec C / C ++ et ses pointeurs, qui IMO, est grandement aggravée par le fait que C / C ++ a toutes ces bizarreries spécifiques au langage.


C ++ 2011 a beaucoup amélioré les choses en ce qui concerne les déclarations de variables.
gnasher729

0

Personnellement, je n'ai pas compris le pointeur même après mon diplôme et après mon premier emploi. La seule chose que je savais, c'est que vous en avez besoin pour les listes chaînées, les arbres binaires et pour passer des tableaux en fonctions. C'était la situation même à mon premier emploi. Ce n'est que lorsque j'ai commencé à donner des interviews que je comprends que le concept de pointeur est profond et qu'il a une utilité et un potentiel énormes. Ensuite, j'ai commencé à lire K & R et à écrire mon propre programme de test. Mon objectif était axé sur l'emploi.
À ce moment, j'ai trouvé que les pointeurs ne sont vraiment pas mauvais ni difficiles s'ils sont enseignés d'une bonne manière. Malheureusement, lorsque j'apprends le C à la fin de mes études, mon professeur n'était pas au courant du pointeur, et même les devoirs utilisaient moins de pointeurs. Au niveau des études supérieures, l'utilisation du pointeur ne consiste vraiment qu'à créer des arbres binaires et des listes liées. Cette pensée que vous n'avez pas besoin d'une bonne compréhension des pointeurs pour travailler avec eux, tue l'idée de les apprendre.


0

Pointeurs .. hah .. tout sur le pointeur dans ma tête, c'est qu'il donne une adresse mémoire où les valeurs réelles de quelque soit sa référence .. donc pas de magie à ce sujet .. si vous apprenez un assemblage vous n'auriez pas beaucoup de mal à apprendre comment fonctionnent les pointeurs .. allez les gars ... même en Java, tout est une référence ..


0

Le principal problème, les gens ne comprennent pas pourquoi ils ont besoin de pointeurs. Parce qu'ils ne sont pas clairs sur la pile et le tas. Il est bon de commencer à partir d'un assembleur 16 bits pour x86 avec un mode mémoire minuscule. Cela a aidé de nombreuses personnes à se faire une idée de la pile, du tas et de "l'adresse". Et octet :) Les programmeurs modernes ne peuvent parfois pas vous dire combien d'octets vous avez besoin pour adresser un espace de 32 bits. Comment peuvent-ils se faire une idée des pointeurs?

Le deuxième moment est la notation: vous déclarez le pointeur comme *, vous obtenez l'adresse comme & et ce n'est pas facile à comprendre pour certaines personnes.

Et la dernière chose que j'ai vue était un problème de stockage: ils comprennent le tas et la pile mais ne peuvent pas se faire une idée de «statique».

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.