En programmation informatique , une référence est une valeur qui permet à un programme d'accéder indirectement à une donnée particulière , telle que la valeur d' une variable ou un enregistrement , dans la mémoire de l' ordinateur ou dans un autre périphérique de stockage . On dit que la référence fait référence à la donnée, et l'accès à la donnée est appelé déréférencement de la référence. Une référence est distincte de la donnée elle-même.
Une référence est un type de données abstrait et peut être implémentée de plusieurs manières. En général, une référence fait référence à des données stockées en mémoire sur un système donné et sa valeur interne est l' adresse mémoire des données, c'est-à-dire qu'une référence est implémentée sous forme de pointeur . Pour cette raison, on dit souvent qu'une référence « pointe vers » les données. D'autres implémentations incluent un décalage (différence) entre l'adresse de la donnée et une adresse « de base » fixe, un index ou un identifiant utilisé dans une opération de recherche dans un tableau ou une table , un handle de système d'exploitation , une adresse physique sur un périphérique de stockage ou une adresse réseau telle qu'une URL .
Représentation formelle
Une référence R est une valeur qui admet une opération, dereference( R ), qui produit une valeur. En général, la référence est typée de manière à renvoyer des valeurs d'un type spécifique, par exemple :
interface Référence < T > { T valeur (); }
Souvent, la référence admet également une opération d'affectation store( R , x ), ce qui signifie qu'il s'agit d'une variable abstraite .
Utiliser
Les références sont largement utilisées en programmation , notamment pour transmettre efficacement des données volumineuses ou modifiables en tant qu'arguments à des procédures , ou pour partager ces données entre différentes utilisations. En particulier, une référence peut pointer vers une variable ou un enregistrement contenant des références à d'autres données. Cette idée est à la base de l'adressage indirect et de nombreuses structures de données liées , telles que les listes chaînées . Les références augmentent la flexibilité dans l'emplacement de stockage des objets, la manière dont ils sont alloués et la manière dont ils sont transmis entre les zones de code . Tant que l'on peut accéder à une référence aux données, on peut accéder aux données par son intermédiaire, et les données elles-mêmes n'ont pas besoin d'être déplacées. Elles facilitent également le partage de données entre différentes zones de code ; chacune conserve une référence à celle-ci.
Les références peuvent entraîner une complexité significative dans un programme, en partie à cause de la possibilité de références aléatoires et sauvages et en partie parce que la topologie des données avec des références est un graphe orienté , dont l'analyse peut être assez compliquée. Néanmoins, les références sont toujours plus simples à analyser que les pointeurs en raison de l'absence d' arithmétique de pointeurs .
Le mécanisme des références, même s'il varie en termes d'implémentation, est une caractéristique fondamentale des langages de programmation, commune à presque tous les langages de programmation modernes. Même certains langages qui ne prennent pas en charge l'utilisation directe des références ont une utilisation interne ou implicite. Par exemple, la convention d' appel par référence peut être implémentée avec une utilisation explicite ou implicite des références.
Exemples
Les pointeurs sont le type de référence le plus primitif. En raison de leur relation intime avec le matériel sous-jacent, ils constituent l'un des types de référence les plus puissants et les plus efficaces. Cependant, en raison également de cette relation, les pointeurs nécessitent une bonne compréhension par le programmeur des détails de l'architecture de la mémoire. Étant donné que les pointeurs stockent l'adresse d'un emplacement mémoire, au lieu d'une valeur directement, une utilisation inappropriée des pointeurs peut conduire à un comportement indéfini dans un programme, notamment en raison de pointeurs suspendus ou de pointeurs sauvages . Les pointeurs intelligents sont des structures de données opaques qui agissent comme des pointeurs mais ne sont accessibles que par des méthodes particulières.
Un handle est une référence abstraite et peut être représenté de différentes manières. Un exemple courant est celui des handles de fichiers (la structure de données FILE dans la bibliothèque d'E/S standard C ), utilisés pour abstraire le contenu d'un fichier. Il représente généralement à la fois le fichier lui-même, comme lors d'une demande de verrouillage sur le fichier, et une position spécifique dans le contenu du fichier, comme lors de la lecture d'un fichier.
En informatique distribuée , la référence peut contenir plus qu'une adresse ou un identifiant ; elle peut également inclure une spécification intégrée des protocoles réseau utilisés pour localiser et accéder à l'objet référencé, la manière dont les informations sont codées ou sérialisées. Ainsi, par exemple, une description WSDL d'un service Web distant peut être considérée comme une forme de référence ; elle comprend une spécification complète de la manière de localiser et de se lier à un service Web particulier . Une référence à un objet distribué en direct est un autre exemple : il s'agit d'une spécification complète de la manière de construire un petit composant logiciel appelé proxy qui s'engagera ensuite dans une interaction peer-to-peer, et par laquelle la machine locale peut accéder à des données qui sont répliquées ou qui n'existent que sous la forme d'un flux de messages faiblement cohérent. Dans tous ces cas, la référence comprend l'ensemble complet d'instructions, ou une recette, permettant d'accéder aux données ; en ce sens, elle remplit le même rôle qu'un identifiant ou une adresse en mémoire.
Si nous avons un ensemble de clés K et un ensemble d'objets de données D , toute fonction bien définie (à valeur unique) de K à D ∪ { null } définit un type de référence, où null est l'image d'une clé ne faisant référence à rien de significatif.
Une représentation alternative d'une telle fonction est un graphe orienté appelé graphe d'accessibilité. Ici, chaque donnée est représentée par un sommet et il existe une arête de u à v si la donnée dans u fait référence à la donnée dans v . Le degré de sortie maximal est de un. Ces graphes sont utiles dans la collecte des déchets , où ils peuvent être utilisés pour séparer les objets accessibles des objets inaccessibles .
Stockage externe et interne
Dans de nombreuses structures de données, les objets volumineux et complexes sont composés d'objets plus petits. Ces objets sont généralement stockés de deux manières :
- Avec le stockage interne, le contenu de l'objet plus petit est stocké à l'intérieur de l'objet plus grand.
- Avec le stockage externe, les objets plus petits sont alloués à leur propre emplacement et l'objet plus grand stocke uniquement les références à ceux-ci.
Le stockage interne est généralement plus efficace, car il y a un coût d'espace pour les références et les métadonnées d'allocation dynamique , et un coût de temps associé au déréférencement d'une référence et à l'allocation de la mémoire pour les objets plus petits. Le stockage interne améliore également la localité de référence en gardant différentes parties du même grand objet proches les unes des autres en mémoire. Cependant, il existe diverses situations dans lesquelles le stockage externe est préférable :
- Si la structure de données est récursive , c'est-à-dire qu'elle peut se contenir elle-même, elle ne peut pas être représentée de manière interne.
- Si l'objet le plus volumineux est stocké dans une zone avec un espace limité, comme la pile, nous pouvons éviter de manquer de stockage en stockant les objets composants volumineux dans une autre région de mémoire et en y faisant référence à l'aide de références.
- Si les objets plus petits peuvent varier en taille, il est souvent gênant ou coûteux de redimensionner l’objet plus grand afin qu’il puisse toujours les contenir.
- Les références sont souvent plus faciles à utiliser et s’adaptent mieux aux nouvelles exigences.
Certains langages, tels que Java , Smalltalk , Python et Scheme , ne prennent pas en charge le stockage interne. Dans ces langages, tous les objets sont accessibles de manière uniforme via des références.
Prise en charge linguistique
Assemblée
En langage assembleur , il est courant d'exprimer des références en utilisant soit des adresses mémoire brutes, soit des index dans des tables. Ces méthodes fonctionnent, mais sont quelque peu délicates à utiliser, car une adresse ne vous dit rien sur la valeur vers laquelle elle pointe, pas même sa taille ou comment l'interpréter ; ces informations sont codées dans la logique du programme. Le résultat est que des interprétations erronées peuvent se produire dans des programmes incorrects, provoquant des erreurs déroutantes.
Zézayer
L'une des premières références opaques était celle de la cellule cons du langage Lisp , qui est simplement un enregistrement contenant deux références à d'autres objets Lisp, y compris éventuellement d'autres cellules cons. Cette structure simple est le plus souvent utilisée pour construire des listes chaînées simples , mais peut également être utilisée pour construire des arbres binaires simples et des listes dites « en pointillés », qui ne se terminent pas par une référence nulle mais par une valeur.
C/C++
Le pointeur est encore aujourd'hui l'un des types de référence les plus populaires. Il est similaire à la représentation en assemblage d'une adresse brute, sauf qu'il porte un type de données statique qui peut être utilisé au moment de la compilation pour garantir que les données auxquelles il fait référence ne sont pas mal interprétées. Cependant, comme le C possède un système de types faible qui peut être violé en utilisant des conversions (conversions explicites entre différents types de pointeurs et entre types de pointeurs et entiers), une mauvaise interprétation est toujours possible, bien que plus difficile. Son successeur, le C++, a essayé d'augmenter la sécurité des types de pointeurs avec de nouveaux opérateurs de conversion, un type de référence
& et des pointeurs intelligents dans sa bibliothèque standard , mais a conservé la possibilité de contourner ces mécanismes de sécurité pour la compatibilité.
Fortran
Fortran ne dispose pas d'une représentation explicite des références, mais les utilise implicitement dans sa sémantique d' appel par référence . Une référence Fortran est mieux considérée comme un alias d'un autre objet, tel qu'une variable scalaire ou une ligne ou une colonne d'un tableau. Il n'existe pas de syntaxe permettant de déréférencer la référence ou de manipuler directement le contenu du référent. Les références Fortran peuvent être nulles. Comme dans d'autres langages, ces références facilitent le traitement de structures dynamiques, telles que des listes chaînées, des files d'attente et des arbres.
Langages orientés objet
Un certain nombre de langages orientés objet tels qu'Eiffel , Java , C # et Visual Basic ont adopté un type de référence beaucoup plus opaque, généralement appelé simplement référence . Ces références ont des types comme des pointeurs C indiquant comment interpréter les données auxquelles elles font référence, mais elles sont de type sécurisé dans le sens où elles ne peuvent pas être interprétées comme une adresse brute et les conversions non sécurisées ne sont pas autorisées. Les références sont largement utilisées pour accéder aux objets et les affecter . Les références sont également utilisées dans les appels de fonctions/ méthodes ou dans le passage de messages, et les comptages de références sont fréquemment utilisés pour effectuer la récupération de place des objets inutilisés.
Langages fonctionnels
Dans Standard ML , OCaml et de nombreux autres langages fonctionnels, la plupart des valeurs sont persistantes : elles ne peuvent pas être modifiées par affectation. Les « cellules de référence » assignables fournissent des variables mutables , des données qui peuvent être modifiées. Ces cellules de référence peuvent contenir n'importe quelle valeur et reçoivent donc le type polymorpheα ref , où αdoit être remplacé par le type de valeur pointée. Ces références mutables peuvent être pointées vers différents objets au cours de leur durée de vie. Par exemple, cela permet de construire des structures de données circulaires. La cellule de référence est fonctionnellement équivalente à un tableau mutable de longueur 1.
Pour préserver la sécurité et l'efficacité des implémentations, les références ne peuvent pas être typées en ML, ni l'arithmétique des pointeurs ne peut être effectuée. Dans le paradigme fonctionnel, de nombreuses structures qui seraient représentées à l'aide de pointeurs dans un langage comme C sont représentées à l'aide d'autres fonctionnalités, telles que le puissant mécanisme de type de données algébrique . Le programmeur peut alors bénéficier de certaines propriétés (comme la garantie d'immuabilité) lors de la programmation, même si le compilateur utilise souvent des pointeurs machine "sous le capot".
Perl/PHP
Perl prend en charge les références matérielles, qui fonctionnent de manière similaire à celles des autres langages, et les références symboliques , qui sont simplement des valeurs de chaîne contenant les noms de variables. Lorsqu'une valeur qui n'est pas une référence matérielle est déréférencée, Perl la considère comme une référence symbolique et donne à la variable le nom donné par la valeur. PHP a une fonctionnalité similaire sous la forme de sa $$varsyntaxe.