Quelle est la différence entre les variables et les pointeurs?

10

En lisant un article décrivant les différences entre OO et la programmation fonctionnelle, je suis tombé sur des pointeurs de fonction. Cela fait un moment que je n'ai pas obtenu mon diplôme en informatique (2003) et j'ai donc cherché des pointeurs pour rafraîchir ma mémoire.

Les pointeurs sont des variables qui contiennent une référence à une adresse mémoire. Ils peuvent être considérés comme pointant vers les données contenues dans cette adresse mémoire si ces données existent. Ou, comme dans le cas de l'article, ils peuvent indiquer le point d'entrée d'une section de code et peuvent être utilisés pour appeler ce code.

Pourquoi est-ce différent d'une variable? Les variables sont des noms symboliques pour les adresses mémoire et les compilateurs remplaceront le nom par l'adresse réelle. Cela signifie que les variables contiennent des références à des emplacements de mémoire et peuvent être considérées comme pointant vers les données à cette adresse si de telles données existent.

Si la différence réside dans le comportement (peut-être qu'un pointeur ne peut pas être réaffecté au moment de l'exécution ou ne peut être affecté qu'à un nom de variable symbolique, pas à une autre valeur), cela ne signifie-t-il pas qu'il s'agit simplement d'une variable d'un type particulier, le type de pointeur? De la même manière, une variable déclarée de type entier est limitée par la compilation dans laquelle elle peut être utilisée.

Qu'est-ce que j'oublie ici?

NectarSoft
la source
4
Rien, un pointeur est effectivement un type dont l'interprétation sémantique est que le contenu de la variable est une adresse. Notez bien sûr qu'il y a deux adresses, il y a l'adresse du pointeur (c'est-à-dire où la variable est stockée) et l'adresse à laquelle le pointeur fait référence (les données réelles à l'adresse de la variable). Un pointeur est un type de référence .
Luke Mathieson

Réponses:

8

Votre question est intéressante à plusieurs égards, car elle nécessite des distinctions soigneuses pour plusieurs questions. Mais votre vision me semble essentiellement correcte. Je n'ai pas lu votre référence avant d'avoir écrit la plupart de cette réponse pour éviter de biaiser ma réponse.

Tout d'abord, votre affirmation Variables are symbolic names for memory addresses, elle est presque correcte, mais confond le concept et sa mise en œuvre habituelle. Une variable est en fait juste un conteneur qui peut contenir une valeur qui peut être modifiée. Habituellement, ce conteneur est implémenté sur un ordinateur comme un bloc d'espace mémoire, caractérisé par une adresse et une taille car les variables peuvent contenir des objets qui nécessitent des représentations avec plus ou moins d'informations.

Mais je considérerai surtout un point de vue plus abstrait de la sémantique des langages, indépendamment des techniques d'implémentation.

Les variables ne sont donc que des conteneurs d'un point de vue abstrait. Un tel conteneur n'a pas besoin d'avoir un nom. Cependant, les langues ont souvent des variables qui sont nommées en leur associant un identifiant, de sorte que les utilisations de la variable peuvent être exprimées par l'identifiant. Une variable peut en fait avoir plusieurs identifiants via différents mécanismes d'alias. Une variable peut également être une sous-partie d'une variable plus grande: un exemple est une cellule d'une variable de tableau, qui peut être nommée en spécifiant la variable de tableau et l'index de la cellule, mais pourrait également être associée à des identifiants via l'aliasing.

J'utilise délibérément le mot container qui est quelque peu neutre, pour éviter d'invoquer d'autres mots qui peuvent être chargés sémantiquement techniquement. Il est en fait proche du concept de référence décrit dans wilipedia , qui est souvent confondu avec une adresse mémoire. Le mot pointeur lui - même est souvent compris comme une adresse mémoire, mais je ne pense pas que cela soit significatif lorsque l'on considère la plupart des langages de haut niveau, et probablement inapproprié dans le document de discussion auquel vous vous référez (bien que les adresses puissent être utilisées), car il est inapproprié se référant à une mise en œuvre spécifique. Cependant, il convient à un langage comme C, qui est censé être beaucoup plus proche des concepts d'implémentation et de l'architecture de la machine.

En fait, si vous regardez des variables ou des valeurs au niveau de l'implémentation, il peut y avoir plusieurs systèmes complexes d'indirection, de "pointeurs de niveau machine", mais qui sont (et devraient être) invisibles pour l'utilisateur, de sorte que le point de vue abstrait Je développe peut être valable. Pour la plupart des langages de programmation, l'utilisateur ne devrait pas avoir à s'inquiéter, ni même à connaître, la mise en œuvre, car la mise en œuvre peut varier considérablement pour un langage donné. Cela peut ne pas être vrai pour certains langages, tels que C, qui sont intentionnellement proches de l'architecture de la machine, en tant que substitut avancé des langages d'assemblage qui sont en relation presque directe avec le codage binaire explicite, mais d'un niveau beaucoup trop bas pour une utilisation pratique dans la plupart des cas. situations.

Ce que l'utilisateur d'une langue doit savoir, et parfois même moins, c'est ce que sont les valeurs et les opérations associées, où elles peuvent être contenues, comment elles peuvent être associées aux noms, comment fonctionne le système de nommage, comment définir des types de valeurs, etc.

Un autre concept important est donc les identifiants et les noms. Nommer une entité (une valeur) peut se faire en associant un identifiant à une valeur (généralement dans une déclaration). Mais une valeur peut également être obtenue en appliquant des opérations à d'autres valeurs nommées. Les noms peuvent être réutilisés et il existe des règles (règles de portée) pour déterminer ce qui est associé à un identifiant donné, selon le contexte d'utilisation. Il existe également des noms spéciaux, appelés littéraux, pour nommer les valeurs de certains domaines, tels que les entiers (par exemple ) ou booléens (par exemple true ).612

L'association d'une valeur immuable avec un identifiant est généralement appelée une constante. Les littéraux sont des constantes dans ce sens.

Les "conteneurs de valeurs" peuvent également être considérés comme des valeurs, et leur association avec un identifiant est une variable au sens "naïf" habituel que vous avez utilisé. Vous pourriez donc dire qu'une variable est une "constante de conteneur".

Maintenant, vous vous demandez peut-être quelle est la différence entre associer un identifiant à une valeur (déclaration de constante) ou affecter une valeur à une variable, c'est-à-dire stocker la valeur dans le conteneur défini comme constante de conteneur. Essentiellement, la déclaration peut être vue comme une opération qui définit une notation, qui associe un identifiant qui est une entité syntaxique à une valeur qui est une entité sémantique. L'affectation est une opération purement sémantique qui modifie un état, c'est-à-dire modifie la valeur d'un conteneur. Dans un certain sens, la déclaration est un méta-concept sans effet sémantique, autre que la fourniture d'un mécanisme de nommage (c'est-à-dire syntaxique) pour les entités sémantiques.

En fait, les affectations sont des opérations sémantiques qui se produisent dynamiquement lors de l'exécution du programme, tandis que les déclarations ont une nature plus syntaxique et doivent généralement être interprétées dans le texte du programme, indépendamment de l'exécution. C'est pourquoi la portée statique (c'est-à-dire la portée textuelle) est généralement le moyen naturel de comprendre la signification des identificateurs.

Après tout cela, je peux dire qu'une valeur de pointeur n'est qu'un autre nom pour un conteneur, et une variable de pointeur est une variable de conteneur, c'est-à-dire un conteneur (constant) qui peut contenir un autre conteneur (avec des limitations possibles sur le jeu contenant imposées par certains système de type).

Concernant le code, vous déclarez [pointers] might indicate the entry point to a section of code and can be used to call that code. En fait, ce n'est pas tout à fait vrai. Une section de code est souvent dénuée de sens seule (d'un point de vue de haut niveau ou d'implémentation). D'un point de vue de haut niveau, le code contient généralement des identifiants, et vous devez interpréter ces identifiants dans le contexte statique où ils ont été déclarés. Mais il existe en fait une duplication possible du même contexte statique, due essentiellement à la récursivité qui est un phénomène dynamique (au moment de l'exécution), et le code ne peut être exécuté que dans une instance dynamique appropriée du contexte statique. C'est un peu complexe, mais la conséquence est que le concept approprié est celui d'une fermeture qui associe un morceau de code et un environnement où les identifiants doivent être interprétés. La fermeture est le concept sémantique approprié, c'est-à-dire une valeur sémantique correctement définissable. Ensuite, vous pouvez avoir des constantes de fermeture, des variables de fermeture,

Une fonction est une fermeture, généralement avec certains paramètres pour définir ou initialiser certaines de ses entités (constantes et variables).

Je saute de nombreuses variations sur les utilisations de ces mécanismes.

Les fermetures peuvent être utilisées pour définir des structures OO dans des langages impératifs ou fonctionnels. En fait, les premiers travaux sur le style OO (probablement avant le nom) ont été effectués de cette façon.

Le document auquel vous faites référence, que j'ai parcouru rapidement, semble être intéressant, rédigé par une personne compétente, mais peut-être pas une lecture facile si vous n'avez pas une expérience significative avec une variété de langues et leurs modèles de calcul sous-jacents.

Mais rappelez-vous: beaucoup de choses sont aux yeux du spectateur, tant qu'il conserve une vision cohérente. Les points de vue peuvent différer.

Est-ce que cela répond à votre question?

PS: C'est une longue réponse. Si vous considérez qu'une partie de celle-ci est inadéquate, veuillez être explicite sur ce qu'elle est. Je vous remercie.

babou
la source
J'ai dû le lire plusieurs fois, mais je pense que cela répond à ma question, oui. Bien que mes définitions soient trop centrées sur l'implémentation, l'idée qu'un pointeur est un type particulier de variable semble être exacte, c'est-à-dire "une valeur de pointeur n'est qu'un autre nom pour un conteneur, et une variable de pointeur est une variable de conteneur" Où je ne le fais pas obtenir la distinction que vous faites est entre un pointeur contenant l'adresse d'un bloc de code et être un conteneur qui contient un conteneur qui contient une fermeture. La distinction entre celle d'un contexte statique et celle d'un programme est-elle en cours d'exécution?
NectarSoft
1
@NectarSoft La distinction n'est qu'entre bloc de code et fermeture. Ce que je dis, c'est qu'en soi, isolé de tout contexte, un bloc de code ne signifie généralement rien. Si je vous dis que « si les mome raths sont plus grands que les borogoves, alors les toves outgrabe », la phrase ne veut rien dire car vous manquez le contexte où tous ces concepts sont définis. Ce contexte statique est généralement le texte entourant le fragment de code. Le problème est que ce contexte statique lui-même peut avoir plusieurs instances d'exécution et que le fragment de code ne doit faire référence qu'à une seule d'entre elles.
babou
1
@NectarSoft (suite) La valeur de fermeture est donc l'association du fragment de code et d'une instance dynamique de contexte statique qui donne un sens à ce fragment. Une association du même fragment de code avec une instance dynamique différente du même contexte statique donne une fermeture différente. En règle générale, votre code peut utiliser une variable appartenant au contexte, mais ce sera une variable différente pour chaque instance dynamique du contexte, bien que son nom (défini statiquement) reste le même. Est-ce que cela clarifie le problème ou dois-je construire un exemple dans la réponse (le format des commentaires est contraint)?
babou
Cela clarifie la question, merci, tout en soulevant d'autres questions sur lesquelles je vais réfléchir. Par exemple, si un pointeur distinct est requis pour chaque fermeture, il semble que le pointeur fasse partie du contexte dynamique et appartient donc à la fermeture! Je m'interroge également sur les limites de fermeture et la hiérarchie de fermeture, car chacun de ces contextes associés à un bloc de code statique et référencé par un pointeur sera nécessairement une variable dans une fermeture qui lui est propre. Tout cela est hors sujet cependant, donc je ferai un peu de lecture et je
poserai
@NectarSoft En fait. lorsque vous créez une fermeture, vous essayez de restreindre le contexte associé au code à ce qui est nécessaire pour donner un sens approprié à ce code (jusqu'à certaines contraintes pratiques pour éviter la micro-gestion des informations). Cela peut être décidé statiquement.
babou
2

La différence est par définition et par application, un pointeur est une variable spécialisée pour contenir une adresse mémoire d'une autre variable; en termes OO, un pointeur serait peut-être considéré comme héritant de son comportement d'une classe générale appelée variable.

Ricardo
la source