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?
Réponses:
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:
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.
la source
Quand j'ai commencé à travailler avec eux, le plus gros problème que j'ai eu était la syntaxe.
sont tous les mêmes.
mais:
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:
Sauf quand vous avez réellement besoin d'un pointeur ... alors vous utilisez une esperluette!
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 .
pour appeler cela, j'ai besoin de l'adresse du tableau de pointeurs vers des pointeurs vers des pointeurs d'entiers:
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)
la source
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.
la source
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[]
fouleC'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:
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 pointer
etpointer 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:
Ou, dans une moindre mesure, quelque chose comme ceci:
Alors en fin de compte, que résolvons-nous vraiment avec tout ce charabia? Rien.
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
-rvalue
sé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é ...
la source
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).
la source
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 .]
la source
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 *p
dans ma tête je dis "p
pointe vers unint
". 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.la source
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.
la source
Voici un exemple de pointeur / tableau qui m'a donné une pause. Supposons que vous ayez deux tableaux:
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:
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.
la source
sizeof(source)
, car sisource
c'est un pointeur,sizeof
ce ne sera pas ce que vous voulez. J'écris parfois (pas toujours)sizeof(source[0]) * number_of_elements_of_source
juste pour rester loin de ce bug.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:
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
&ch
que signifiait réellement et pourquoistr
n'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:
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
malloc
etnew
, 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
malloc
retour 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 codemalloc
pourrait 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 dontmalloc
fonctionne 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 à:
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:
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.
la source
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.
É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.
Allocation de mémoire dynamique unidimensionnelle. Comprendre l'allocation de mémoire 1-D m'a fait comprendre le concept du pointeur.
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.
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.
la source
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
&i
signifiait «l'adresse de i», alors comprendre que cela*i
signifiait «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.
la source
&i
est l'adresse et*i
le contenu, qu'est-ce que c'esti
?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.
la source
void foo(Clazz obj) { obj = new Clazz(); }
un no-op alors quevoid bar(Clazz obj) { obj.quux = new Quux(); }
mute l'argument ...Voici une non-réponse: utilisez cdecl (ou c ++ decl) pour le comprendre:
la source
Ils ajoutent une dimension supplémentaire au code sans modification significative de la syntaxe. Penses-y:
Il n'y a qu'une seule chose à changer:
a
. Vous pouvez écrirea = 6
et les résultats sont évidents pour la plupart des gens. Mais maintenant, considérez:Il y a deux choses
a
qui sont pertinentes à des moments différents: la valeur réelle dea
, le pointeur et la valeur «derrière» le pointeur. Vous pouvez changera
:... et
some_int
est toujours quelque part avec la même valeur. Mais vous pouvez également changer la chose vers laquelle il pointe:Il y a un fossé conceptuel entre
a = 6
, qui n'a que des effets secondaires locaux, et*a = 6
qui 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, localea
ou indirecte avec*a
... c'est peut-être ce qui déroute les gens.la source
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.
la source
Les pointeurs sont difficiles à cause de l'indirection.
la source
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.
la source
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.
la source
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:
En C, vous définiriez une liste liée d'entiers comme ceci:
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:
Assez simple - une liste est soit vide, soit formée à partir d'une valeur et du reste de la liste.
Voici comment appliquer une fonction
foo
à chaque caractère d'une chaîne en 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.
Voici une véritable signature de fonction trouvée dans glib :
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 ilmap
est déclaré dans Haskell:C'est beaucoup plus simple:
map
c'est une fonction qui prend une fonction qui convertit una
en ab
, et l'applique à une liste dea
's pour produire une liste deb
' s. Tout comme dans la fonction Cg_list_foreach
,map
n'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".
la source
c != NULL
votre exemple "Hello world" ... vous voulez dire*c != '\0'
.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.
la source
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? ;-)
la source
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.
la source
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.
la source
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!
la source
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:
contre.
contre.
et apparemment
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
alors le & a est une adresse mémoire.
OTOH, si c'est
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
peut être un pointeur vers un int
ou
peut être un tableau vers un entier
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.
la source
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.
la source
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 ..
la source
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».
la source