Une introduction aux pointeurs pour les programmeurs

Une introduction aux pointeurs pour les programmeurs / La programmation

Que vous vous en rendiez compte ou non, la grande majorité des programmes que vous avez utilisés utilisent des pointeurs d'une manière ou d'une autre. Peut-être avez-vous vécu une NullPointerException à un moment donné. En tant que programmeur, le code que vous écrivez utilisera probablement des pointeurs, même si vous ne les avez pas mis en œuvre vous-même..

Aujourd'hui, je vais vous montrer comment fonctionnent les pointeurs. Par conséquent, vous voudrez peut-être vérifier le fonctionnement des tableaux et des listes. Fonctionnement des tableaux et des listes en Python Mode d'utilisation des tableaux et des listes en Python Les tableaux et les listes figurent parmi les structures de données les plus utiles en programmation: - bien que peu de gens les utilisent au maximum de leurs possibilités. Lisez la suite pour une introduction à la programmation. Cet article sera plus théorique que d'habitude, mais restez-y, les pointeurs sont très complexes!

Code de compilation

Avant de creuser dans les pointeurs, vous devez comprendre comment le code est construit et exécuté - vous le savez peut-être déjà. Cette section aura des déclarations assez générales - les choses qui s’appliquent au majorité des langues, mais pas nécessairement toutes.

Reprenons les choses au début. Chaque ordinateur utilise le binaire Qu'est-ce que le binaire? [Technologie expliquée] Qu'est-ce que le binaire? [Technologie expliquée] Étant donné que le binaire est si fondamental pour l’existence des ordinateurs, il semble étrange que nous n’ayons jamais abordé le sujet auparavant. C’est pourquoi aujourd’hui, je pensais vous donner un bref aperçu de ce que… Read More, une série de uns et de zéros qui composent la technologie moderne telle que nous la connaissons. Il est extrêmement difficile de coder quoi que ce soit en binaire (les fichiers seraient très déroutants), car ce sont les instructions brutes nécessaires à votre unité centrale de traitement ou un processeur pour fonctionner Qu'est-ce qu'un processeur et que fait-il? Qu'est-ce qu'un processeur et que fait-il? Les acronymes informatiques sont source de confusion. Qu'est-ce qu'un processeur quand même? Et ai-je besoin d'un processeur quad ou dual-core? Qu'en est-il d'AMD ou d'Intel? Nous sommes là pour vous aider à expliquer la différence! Lire la suite . Ceci est connu comme Langage machine.

La prochaine étape à partir du code machine est Assemblée. C'est un format quelque peu lisible par l'homme. Bien que la programmation soit encore complexe, c'est possible. L’ensemble est composé d’une série de commandes simples permettant d’exécuter des tâches. Il est appelé niveau faible langage de programmation. Il est possible d'écrire des programmes complexes, mais il est difficile d'exprimer des concepts abstraits et nécessite beaucoup de réflexion..

Beaucoup de jeux vidéo et d’applications hautes performances ont une partie de la logique écrite lors de l’assemblage, car certaines augmentations de vitesse réelles peuvent être trouvées si vous savez ce que vous faites. Cependant, pour la grande majorité des projets de programmation, vous n'avez pas besoin de connaître d'assemblage..

Ainsi, si le code machine est trop difficile à écrire et que l’assemblage est trop difficile à programmer, avec quoi écrivez-vous? Voici où haut niveau les langues entrent en jeu. Les langues de haut niveau rendent les programmes faciles à écrire. Vous pouvez programmer dans quelque chose qui ressemble à votre langue maternelle, et il est facile d’exprimer des algorithmes complexes. Vous avez peut-être entendu parler de nombreux langages de haut niveau (et vous aurez certainement utilisé un programme écrit en eux):

  • DE BASE
  • C++
  • Zézayer

Ces langues sont très anciennes maintenant et beaucoup ont été développées au début des années 1950! Presque tous les langages de programmation modernes sont des langages de haut niveau, y compris PHP et Python. De plus en plus de langues sont inventées chaque jour (même s’il en existe probablement assez à présent), mais comment fonctionne exactement votre code quand les ordinateurs ont besoin de code machine??

Voici où la compilation entre en jeu. Un compilateur est un programme qui convertit votre code de haut niveau en une forme pouvant être exécutée. Cela pourrait être un autre langage de haut niveau, mais il s’agit généralement d’assemblage. Certains langages (tels que Python ou Java) convertissent votre code en une étape intermédiaire appelée bytecode. Cela nécessitera une nouvelle compilation à une date ultérieure, ce qui est généralement fait à la demande, par exemple lorsque le programme est exécuté. Ceci est connu comme juste à temps compilation, et c'est assez populaire.

Gestion de la mémoire

Maintenant que vous savez comment fonctionnent les langages de programmation, examinons la gestion de la mémoire dans les langages de haut niveau. Pour ces exemples, je vais utiliser un pseudo-code - code écrit non dans un langage spécifique, mais utilisé pour montrer des concepts plutôt que la syntaxe exacte. Aujourd'hui, cela ressemblera surtout au C ++ car c'est le meilleur langage de haut niveau (à mon avis).

Pour cette section, cela vous aidera si vous comprenez le fonctionnement de la mémoire RAM. Guide rapide et simpliste de la mémoire vive: Ce que vous devez savoir Guide pratique et rapide de la mémoire RAM: ce que vous devez savoir La mémoire vive est un élément essentiel de tout ordinateur. , mais il peut être déroutant de comprendre si vous n'êtes pas un gourou de la technologie. Dans cet article, nous le décomposons en termes faciles à comprendre. Lire la suite .

La plupart des langues ont des variables - des conteneurs qui stockent des données. Vous devez définir explicitement le type de données. Certains langages à typage dynamique tels que Python ou PHP gèrent cela pour vous, mais ils doivent le faire..

Disons que vous avez une variable:

int myNumber;

Ce code déclare une variable appelée mon numéro, et lui donne un type de données de entier. Une fois compilé, l'ordinateur interprète cette commande comme suit:

“Trouver de la mémoire vide et réserver un espace assez grand pour stocker un entier”

Une fois cette commande exécutée, ce bit de mémoire ne peut plus être utilisé par un autre programme. Il ne contient pas encore de données, mais il est réservé à votre variable myNumber..

Assignez maintenant une valeur à votre variable:

myNumber = 10;

Pour terminer cette tâche, votre ordinateur accède à son emplacement de mémoire réservé et modifie, quelle que soit la valeur qui y est stockée, cette nouvelle valeur..

Tout cela est bien beau, mais comment les emplacements de mémoire ne sont-ils pas réservés? Si les programmes réservaient toute la mémoire qu’ils appréciaient, la mémoire vive se remplirait immédiatement - ce qui en ferait un très système lent.

Pour éviter ce problème potentiel, de nombreuses langues implémentent une Éboueur, utilisé pour détruire les variables (et donc libérer les emplacements de mémoire réservés) qui ont disparu hors de portée.

Vous vous demandez peut-être ce qu'est la portée et pourquoi c'est si important. Scope définit les limites et la durée de vie des variables ou de la mémoire utilisée par un programme. Une variable est “hors de portée” lorsqu'il n'est plus accessible par aucun code (c'est à ce moment-là que le ramasse-miettes intervient). Voici un exemple:

fonction maths () int firstNumber = 1;  int secondNumber = 2; print (firstNumber + secondNumber); // ne fonctionnera pas

Cet exemple ne compilera pas. La variable firstNumber est dans le mathématiques fonction, donc c'est sa portée. Il est impossible d'y accéder de l'extérieur de la fonction dans laquelle il a été déclaré. C'est un concept de programmation important, et comprendre, il est essentiel de travailler avec des pointeurs.

Cette façon de gérer la mémoire est appelée la empiler. C'est ainsi que fonctionne la grande majorité des programmes. Vous n'avez pas besoin de comprendre les indicateurs pour l'utiliser, et c'est assez bien structuré. L'inconvénient de la pile est la vitesse. Comme l'ordinateur doit attribuer de la mémoire, garder une trace des variables et exécuter le nettoyage de la mémoire, il y a une petite surcharge. C'est bien pour les petits programmes, mais qu'en est-il des tâches hautes performances ou des applications gourmandes en données??

Entrez: pointeurs.

Pointeurs

En surface, les pointeurs semblent simples. Ils font référence (pointer vers) un emplacement en mémoire. Cela peut ne pas sembler différent de “ordinaire” variables sur la pile, mais croyez-moi, il y a une énorme différence. Les pointeurs sont stockés sur le tas. C'est le contraire de la pile - c'est moins organisé, mais c'est beaucoup plus rapide.

Regardons comment les variables sont assignées sur la pile:

int numberOne = 1; int numberTwo = numberOne;

C'est une syntaxe simple. La variable numéro deux contient le numéro un. Sa valeur est copiée au cours de l’affectation du numéro un variable.

Si vous vouliez avoir le adresse mémoire d'une variable, au lieu de sa valeur, vous devez utiliser le signe esperluette (&). Ceci est appelé le adresse de opérateur, et est une partie essentielle de votre boîte à outils de pointeur.

int numberOne = 1; int numberTwo = & numberOne;

Maintenant le numéro deux variable points dans un emplacement de mémoire, au lieu de copier le numéro un dans son propre emplacement de mémoire. Si vous deviez sortir cette variable, ce ne serait pas le numéro un (même si cela est stocké dans l'emplacement de mémoire). Il afficherait son emplacement mémoire (probablement environ 2167, bien que cela varie en fonction du système et de la mémoire RAM disponible). Pour accéder à la valeur stockée dans un pointeur, au lieu de l'emplacement de la mémoire, vous devez déréférence le pointeur. Cela accède directement à la valeur, qui serait le numéro un dans ce cas. Voici comment déréférencer un pointeur:

int numberTwo = * numberOne;

le opérateur de déréférence est un astérisque (*).

Cela peut être un concept difficile à comprendre, alors reprenons-le:

  • le adresse de l'opérateur (&) stocke l'adresse mémoire.
  • le opérateur de déréférence (*) accède à la valeur.

La syntaxe change légèrement lors de la déclaration de pointeurs:

int * myPointer;

Le type de données de int se réfère ici au type de données du pointeur points to, et non le type du pointeur lui-même.

Maintenant que vous savez ce que sont les pointeurs, vous pouvez réaliser de très astuces avec eux! Lorsque la mémoire est utilisée, votre système d'exploitation démarre. séquentiellement. Vous pouvez considérer la RAM comme un casier. Beaucoup de trous pour stocker quelque chose, un seul peut être utilisé à la fois. La différence est que ces casiers sont tous numérotés. Lors de l’attribution de mémoire, votre système d’exploitation démarre au numéro le plus bas et progresse. Il ne sautera jamais entre des nombres aléatoires.

Lorsque vous travaillez avec des pointeurs, si vous avez assigné un tableau, vous pouvez facilement naviguer vers l'élément suivant en incrémentant simplement votre pointeur..

Voici où ça devient intéressant. Lorsque vous transmettez des valeurs à une fonction (à l'aide de variables stockées dans la pile), ces valeurs sont copiées dans votre fonction. Si ce sont de grandes variables, votre programme les stocke maintenant deux fois. Lorsque votre fonction est terminée, vous aurez peut-être besoin d'un moyen de renvoyer ces valeurs. Les fonctions ne peuvent généralement renvoyer qu’une chose - qu’en est-il si vous voulez renvoyer deux, trois ou quatre choses?

Si vous passez un pointeur sur votre fonction, seule l'adresse de la mémoire est copiée (ce qui est minuscule). Cela évite beaucoup de travail à votre CPU! Peut-être que votre pointeur pointe vers un énorme tableau d'images - non seulement votre fonction peut-elle fonctionner avec exactement les mêmes données stockées dans le même emplacement mémoire, mais une fois que c'est fait, il n'est pas nécessaire de retourner quoi que ce soit. Soigné!

Vous devez cependant faire très attention. Les pointeurs peuvent toujours sortir du cadre et être récupérés par le ramasse-miettes. Les valeurs stockées en mémoire ne sont toutefois pas collectées. Ceci s'appelle une fuite de mémoire. Vous ne pouvez plus accéder aux données (les pointeurs ayant été détruits), mais la mémoire est toujours utilisée. C’est une raison courante pour laquelle de nombreux programmes se bloquent et peuvent échouer de façon spectaculaire s’il ya une grande quantité de données. La plupart du temps, votre système d'exploitation tuera votre programme si vous avez une fuite importante (utilisez plus de RAM que le système), mais ce n'est pas souhaitable..

Le débogage des pointeurs peut être un cauchemar, en particulier si vous travaillez avec de grandes quantités de données ou si vous travaillez en boucle. Leurs inconvénients et leur difficulté à comprendre valent vraiment les compromis que vous gagnez en performance. Bien que rappelez-vous, ils ne sont pas toujours nécessaires.

C'est tout pour aujourd'hui. J'espère que vous avez appris quelque chose d'utile sur un sujet complexe. Bien sûr, nous n’avons pas couvert tout ce qu’il ya à savoir - c’est un sujet très complexe. Si vous souhaitez en savoir plus, je vous recommande vivement C ++ dans 24 heures..

Si cela vous semble un peu complexe, consultez notre guide des langages de programmation les plus simples. 6 Langages de programmation les plus faciles à apprendre pour les débutants 6 Langages de programmation les plus simples à apprendre pour les débutants Apprendre à programmer consiste à trouver le bon langage autant que le processus d'édification. Voici les six langages de programmation les plus faciles pour les débutants. Lire la suite .

Avez-vous appris comment fonctionnent les pointeurs aujourd'hui? Avez-vous des conseils et astuces que vous souhaitez partager avec d'autres programmeurs? Sautez dans les commentaires et partagez vos pensées ci-dessous!

En savoir plus sur: Programmation.