web-dev-qa-db-fra.com

Quelle est la différence entre une référence C # et un pointeur?

Je ne comprends pas très bien la différence entre une référence C # et un pointeur. Ils pointent tous les deux vers un endroit en mémoire, n'est-ce pas? La seule différence que je peux comprendre est que les pointeurs ne sont pas aussi intelligents, ne peuvent pointer vers rien sur le tas, sont exemptés de la récupération de place et ne peuvent référencer que des structures ou des types de base.

L'une des raisons pour lesquelles je demande est qu'il y a une perception selon laquelle les gens doivent bien comprendre les pointeurs (de C, je suppose) pour être un bon programmeur. Beaucoup de gens qui apprennent des langues de niveau supérieur ne le savent pas et ont donc cette faiblesse.

Je ne comprends tout simplement pas ce qui est si complexe avec un pointeur? C'est simplement une référence à une place dans la mémoire, n'est-ce pas? Il peut retourner son emplacement et interagir directement avec l'objet à cet emplacement?

Ai-je raté un gros point?

76
Richard

Les références C # peuvent et seront déplacées par le garbage collector mais les pointeurs normaux sont statiques. C'est pourquoi nous utilisons le mot clé fixed lors de l'acquisition d'un pointeur vers un élément de tableau, pour éviter qu'il ne soit déplacé.

EDIT: Conceptuellement, oui. Ils sont plus ou moins les mêmes.

42
Mehrdad Afshari

Une référence est un pointeur "abstrait": vous ne pouvez pas faire d'arithmétique avec une référence et vous ne pouvez pas jouer de tours de bas niveau avec sa valeur.

12
Chris Conway

Je pense d'abord que vous devez définir un "pointeur" dans votre sémantique. Voulez-vous dire le pointeur que vous pouvez créer dans un code dangereux avec fixe ? Voulez-vous dire un IntPtr que vous obtenez peut-être d'un appel natif ou Marshal.AllocHGlobal ? Voulez-vous dire un GCHandle ? Tous sont essentiellement la même chose - une représentation d'une adresse mémoire où quelque chose est stocké - que ce soit une classe, un nombre, une structure, peu importe. Et pour mémoire, ils peuvent certainement être sur le tas.

Un pointeur (toutes les versions ci-dessus) est un élément fixe. Le GC n'a aucune idée de ce qui se trouve à cette adresse et n'a donc pas la capacité de gérer la mémoire ou la vie de l'objet. Cela signifie que vous perdez tous les avantages d'un système de collecte des ordures. Vous devez gérer manuellement la mémoire de l'objet et vous avez le potentiel de fuites.

Une référence d'autre part est à peu près un "pointeur géré" que le GC connaît. C'est toujours une adresse d'un objet, mais maintenant le GC connaît les détails de la cible, il peut donc le déplacer, faire des compactions, finaliser, éliminer et toutes les autres choses sympas qu'un environnement managé fait.

La principale différence réside vraiment dans la manière et la raison de leur utilisation. Pour la grande majorité des cas dans un langage géré, vous allez utiliser une référence d'objet. Les pointeurs deviennent pratiques pour faire de l'interopérabilité et le rare besoin d'un travail vraiment rapide.

Edit: En fait voici un bon exemple de quand vous pourriez utiliser un "pointeur" dans le code managé - dans ce cas c'est un GCHandle, mais la même chose aurait pu être faite avec AllocHGlobal ou en utilisant fixed sur un tableau d'octets ou une structure. J'ai tendance à préférer le GCHandle car il me semble plus ".NET".

6
ctacke

Les pointeurs pointent vers un emplacement dans l'espace d'adressage mémoire. Les références pointent vers une structure de données. Les structures de données sont toutes déplacées tout le temps (enfin, pas si souvent, mais de temps en temps) par le garbage collector (pour compacter l'espace mémoire). De plus, comme vous l'avez dit, les structures de données sans références seront récupérées après un certain temps.

De plus, les pointeurs ne sont utilisables que dans un contexte dangereux.

5
Tamas Czinege

Une différence majeure entre une référence et un pointeur est qu'un pointeur est une collection de bits dont le contenu n'a d'importance que lorsqu'il est activement utilisé comme pointeur, tandis qu'une référence encapsule non seulement un ensemble de bits, mais aussi des métadonnées qui cadre sous-jacent informé de son existence. Si un pointeur existe vers un objet en mémoire et que cet objet est supprimé mais que le pointeur n'est pas effacé, l'existence continue du pointeur ne causera aucun dommage à moins ou jusqu'à ce qu'une tentative soit faite pour accéder à la mémoire vers laquelle il pointe. Si aucune tentative n'est faite pour utiliser le pointeur, rien ne se souciera de son existence. En revanche, les frameworks basés sur des références comme .NET ou la JVM nécessitent que le système puisse toujours identifier chaque référence d'objet existante, et chaque référence d'objet existante doit toujours être null ou bien identifier un objet de son type propre.

Notez que chaque référence d'objet encapsule en fait deux types d'informations: (1) le contenu du champ de l'objet qu'il identifie, et (2) l'ensemble d'autres références au même objet. Bien qu'il n'y ait aucun mécanisme par lequel le système peut identifier rapidement toutes les références qui existent à un objet, l'ensemble d'autres références qui existent à un objet peut souvent être la chose la plus importante encapsulée par une référence (ceci est particulièrement vrai lorsque les choses de type Object sont utilisées comme des choses comme des jetons de verrouillage). Bien que le système conserve quelques bits de données pour chaque objet à utiliser dans GetHashCode, les objets n'ont pas de véritable identité au-delà de l'ensemble de références qui leur existe. Si X contient la seule référence existante à un objet, remplacer X par une référence à un nouvel objet avec le même contenu de champ n'aura aucun effet identifiable sauf pour changer les bits renvoyés par la fonction GetHashCode(), et même cet effet n'est pas garanti.

5
supercat

Je pense qu'il est important pour les développeurs de comprendre le concept d'un pointeur, c'est-à-dire de comprendre l'indirection. Cela ne signifie pas qu'ils doivent nécessairement utiliser des pointeurs. Il est également important de comprendre que le concept de référence diffère du concept de pointeur, bien que subtilement, mais que la mise en œuvre d'une référence soit presque toujours est un pointeur.

C'est-à-dire qu'une variable contenant une référence n'est qu'un bloc de mémoire de la taille d'un pointeur contenant un pointeur sur l'objet. Cependant, cette variable ne peut pas être utilisée de la même manière qu'une variable pointeur peut être utilisée. En C # (et C, et C++, ...), un pointeur peut être indexé comme un tableau, mais pas une référence. En C #, une référence est suivie par le garbage collector, un pointeur ne peut pas l'être. En C++, un pointeur peut être réaffecté, pas une référence. Syntaxiquement et sémantiquement, les pointeurs et les références sont assez différents, mais mécaniquement, ils sont les mêmes.

4
P Daddy

Un pointeur peut pointer vers n'importe quel octet dans l'espace d'adressage de l'application. Une référence est étroitement contrainte et contrôlée et gérée par l'environnement .NET.

4
jdigital

Ce qui rend les pointeurs quelque peu complexes, ce n'est pas ce qu'ils sont, mais ce que vous pouvez en faire. Et quand vous avez un pointeur vers un pointeur vers un pointeur. C'est là que ça commence vraiment à s'amuser.

1
Robert C. Barth

L'un des plus grands avantages des références par rapport aux pointeurs est une plus grande simplicité et lisibilité. Comme toujours, lorsque vous simplifiez quelque chose, vous le rendez plus facile à utiliser, mais au détriment de la flexibilité et du contrôle, vous obtenez des éléments de bas niveau (comme d'autres l'ont mentionné).

Les pointeurs sont souvent critiqués pour être "laids".

class* myClass = new class();

Maintenant, chaque fois que vous l'utilisez, vous devez d'abord le déréférencer en

myClass->Method() or (*myClass).Method()

Malgré la perte de lisibilité et l'ajout de complexité, les gens devaient encore souvent utiliser des pointeurs comme paramètres afin de pouvoir modifier l'objet réel (au lieu de passer par valeur) et pour le gain de performances de ne pas avoir à copier d'énormes objets.

Pour moi, c'est pourquoi les références sont "nées" en premier lieu pour offrir le même avantage que les pointeurs mais sans toute cette syntaxe de pointeur. Vous pouvez maintenant transmettre l'objet réel (pas seulement sa valeur) ET vous avez une manière plus lisible et normale d'interagir avec l'objet.

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

Les références C++ différaient de C #/Java fait référence en ce qu'une fois que vous lui avez attribué une valeur, vous ne pouviez pas la réaffecter (et elle doit être affectée lorsqu'elle a été déclarée)) Cela revenait à utiliser un pointeur const (un pointeur qui ne pouvait pas être redirigé vers un autre objet).

Java et C # sont des langages modernes de très haut niveau qui ont nettoyé beaucoup de désordres qui s'étaient accumulés en C/C++ au fil des ans et les pointeurs étaient certainement l'une de ces choses qui devaient être `` nettoyées ''.

Dans la mesure où votre commentaire sur la connaissance des pointeurs fait de vous un programmeur plus fort, cela est vrai dans la plupart des cas. Si vous savez "comment" quelque chose fonctionne plutôt que de simplement l'utiliser sans le savoir, je dirais que cela peut souvent vous donner un avantage. La quantité d'un Edge variera toujours. Après tout, utiliser quelque chose sans savoir comment il est implémenté est l'une des nombreuses beautés de OOP et Interfaces.

Dans cet exemple spécifique, qu'est-ce que le fait de savoir sur les pointeurs vous aiderait avec les références? Comprendre qu'une référence C # n'est PAS l'objet lui-même mais pointe vers l'objet est un concept très important.

# 1: Vous ne passez PAS par valeur Eh bien pour les débutants lorsque vous utilisez un pointeur, vous savez que le pointeur ne contient qu'une adresse, c'est tout. La variable elle-même est presque vide et c'est pourquoi il est si agréable de passer comme arguments. En plus du gain de performances, vous travaillez avec l'objet réel de sorte que les modifications que vous apportez ne sont pas temporaires

# 2: Polymorphisme/Interfaces Lorsque vous avez une référence qui est un type d'interface et qu'elle pointe vers un objet, vous ne pouvez appeler que des méthodes de cette interface même si l'objet peut avoir beaucoup plus de capacités. Les objets peuvent également implémenter différemment les mêmes méthodes.

Si vous comprenez bien ces concepts, je ne pense pas que vous manquez trop de ne pas avoir utilisé de pointeurs. Le C++ est souvent utilisé comme langage d'apprentissage de la programmation car il est parfois bon de se salir les mains. De plus, travailler avec des aspects de niveau inférieur vous fait apprécier le confort d'une langue moderne. J'ai commencé avec C++ et je suis maintenant programmeur C # et j'ai vraiment l'impression que travailler avec des pointeurs bruts m'a aidé à mieux comprendre ce qui se passe sous le capot.

Je ne pense pas qu'il soit nécessaire que tout le monde commence par des pointeurs, mais ce qui est important, c'est qu'ils comprennent pourquoi les références sont utilisées à la place des types de valeur et la meilleure façon de comprendre cela est de regarder son ancêtre, le pointeur.

1
Despertar