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 ont tous deux une place 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 rien pointer sur le tas, sont exempts de la récupération de place et ne peuvent faire référence qu’à des structures ou à des types de base.

L’une des raisons pour lesquelles je pose la question 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 personnes qui apprennent des langages de haut niveau manquent cela et ont donc cette faiblesse.

Je n’ai tout simplement pas ce qu’il y a de si complexe sur un pointeur? C’est simplement une référence à un lieu en mémoire, n’est-ce pas? Il peut retourner son emplacement et interagir avec l’object directement à cet endroit?

Ai-je raté un point énorme?

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 fixed mot-clé fixed lors de l’acquisition d’un pointeur sur un élément du tableau, pour l’empêcher d’être déplacé.

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

Il existe une distinction légère, mais extrêmement importante, entre un pointeur et une référence. Un pointeur pointe vers un emplacement en mémoire tandis qu’une référence pointe vers un object en mémoire. Les pointeurs ne sont pas “de type sécurisé” dans le sens où vous ne pouvez pas garantir l’exactitude de la mémoire qu’ils pointent.

Prenons par exemple le code suivant

 int* p1 = GetAPointer(); 

Ce type est sûr dans la mesure où GetAPointer doit renvoyer un type compatible avec int *. Pourtant, il n’y a toujours aucune garantie que * p1 pointe réellement vers un int. Ce pourrait être un caractère double, ou simplement un pointeur dans la mémoire aléatoire.

Une référence pointe cependant sur un object spécifique. Les objects peuvent être déplacés en mémoire, mais la référence ne peut pas être invalidée (sauf si vous utilisez un code non sécurisé). Les références sont beaucoup plus sûres à cet égard que les pointeurs.

 ssortingng str = GetASsortingng(); 

Dans ce cas, str a l’un des deux états 1) il ne pointe vers aucun object et est donc nul ou 2) il pointe vers une chaîne valide. C’est tout. Le CLR garantit que ce soit le cas. Il ne peut pas et ne veut pas pour un pointeur.

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 d’astuces de bas niveau avec sa valeur.

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 en code non sécurisé avec fixe ? Voulez-vous dire un IntPtr que vous obtenez peut-être d’un appel natif ou Marshal.AllocHGlobal ? Voulez-vous dire un GCHandle ? Le tout est 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 qu’il y a à cette adresse et n’a donc aucune capacité à gérer la mémoire ou la vie de l’object. Cela signifie que vous perdez tous les avantages d’un système de collecte des ordures. Vous devez gérer manuellement la mémoire d’object et vous risquez des fuites.

Par contre, une référence est à peu près un “pointeur géré” que le GC connaît. C’est toujours une adresse d’un object, mais maintenant le GC connaît les détails de la cible, donc il peut le déplacer, faire des compactions, finaliser, éliminer et tout ce que fait un environnement géré.

La principale différence réside dans le fait de savoir comment et pourquoi les utiliser. Pour la grande majorité des cas dans un langage géré, vous allez utiliser une référence d’object. Les pointeurs deviennent pratiques pour faire de l’interopérabilité et le besoin rare d’un travail vraiment rapide.

Edit: En fait, voici un bon exemple de l’utilisation d’un “pointeur” en code managé – dans ce cas, il s’agit d’un GCHandle, mais la même chose aurait pu être faite avec AllocHGlobal ou en utilisant un tableau ou une structure d’octets. J’ai tendance à préférer le GCHandle car il me semble plus “NET”.

Les pointeurs pointent vers un emplacement dans l’espace d’adressage de la mémoire. Les références pointent vers une structure de données. Les structures de données ont toutes bougé tout le temps (enfin, pas si souvent, mais de temps en temps) par le ramasse-miettes (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.

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 , même s’il n’est que subtil, mais que l’implémentation d’une référence est presque toujours un pointeur.

C’est-à-dire qu’une variable contenant une référence est juste un bloc de mémoire de la taille d’un pointeur contenant un pointeur sur l’object. Cependant, cette variable ne peut pas être utilisée de la même manière qu’une variable de 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é, une référence ne peut pas. Syntaxiquement et sémantiquement, les pointeurs et les références sont très différents, mais mécaniquement, ils sont identiques.

Un pointeur peut pointer sur un 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.

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 s’il est utilisé activement en tant que pointeur, alors qu’une référence encapsule non seulement un ensemble de bits, mais aussi certaines métadonnées cadre sous-jacent informé de son existence. Si un pointeur existe pour un object en mémoire et que cet object est supprimé mais que le pointeur n’est pas effacé, l’existence continue du pointeur ne causera aucun dommage que si une tentative d’access à la mémoire est tentée. Si aucune tentative n’est faite pour utiliser le pointeur, rien ne sera concerné par son existence. En revanche, les frameworks basés sur des références tels que .NET ou JVM exigent qu’il soit toujours possible pour le système d’identifier chaque référence d’object existante, et chaque référence d’object existante doit toujours être null ou identifier un object du type approprié. .

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

Ce qui les rend quelque peu complexes, ce n’est pas ce qu’elles sont, mais ce que vous pouvez en faire. Et lorsque vous avez un pointeur sur un pointeur vers un pointeur. C’est alors que ça commence vraiment à s’amuser.

L’un des principaux avantages des références par rapport aux pointeurs est une plus grande simplicité et lisibilité. Comme toujours, lorsque vous simplifiez quelque chose, vous simplifiez l’utilisation, mais au désortingment de la flexibilité et du contrôle que vous obtenez avec les fonctionnalités de bas niveau (comme d’autres personnes 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, soit par

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

En dépit de la perte de lisibilité et de la complexité, les utilisateurs devaient toujours utiliser des pointeurs souvent comme parameters pour pouvoir modifier l’object réel (au lieu de passer par la valeur) et gagner en performance sans avoir à copier d’énormes objects.

Pour moi, c’est la raison pour laquelle les références sont «nées» en premier lieu pour offrir les mêmes avantages que les pointeurs, mais sans toute cette syntaxe de pointeur. Maintenant, vous pouvez passer l’object réel (pas seulement sa valeur) ET vous avez un moyen plus lisible d’interagir avec l’object.

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

Les références C ++ diffèrent des références C # / Java dans la mesure où une fois que vous leur atsortingbuez une valeur, vous ne pouvez pas la réaffecter (et elle doit être affectée lors de sa déclaration). C’était la même chose que d’utiliser un pointeur const (un pointeur qui ne pouvait pas être redirigé vers un autre object).

Java et C # sont de très haut niveau, les langages modernes qui nettoyaient beaucoup des erreurs accumulées en C / C ++ au fil des ans et les pointeurs étaient certainement l’un de ceux qu’il fallait “nettoyer”.

Dans la mesure où votre commentaire sur la connaissance des pointeurs vous rend plus fort programmeur, cela est vrai dans la plupart des cas. Si vous savez comment quelque chose fonctionne plutôt que de l’utiliser sans savoir, je dirais que cela peut souvent vous donner un avantage. Combien de bord variera toujours. Après tout, utiliser quelque chose sans savoir comment elle est mise en œuvre est l’une des nombreuses beautés de la POO et des interfaces.

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

# 1: Vous ne passez PAS par valeur 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 en argument. En plus du gain de performance, vous travaillez avec l’object réel, donc toutes 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 object, vous ne pouvez appeler des méthodes de cette interface que si l’object peut avoir beaucoup plus de capacités. Les objects peuvent également implémenter les mêmes méthodes différemment.

Si vous comprenez bien ces concepts, je ne pense pas que vous manquiez trop de ne pas avoir utilisé de pointeurs. C ++ est souvent utilisé comme langage pour l’apprentissage de la programmation, car il est parfois utile de se salir les mains. En outre, travailler avec des aspects de niveau inférieur vous permet d’apprécier le confort d’une langue moderne. J’ai commencé avec C ++ et je suis maintenant programmeur en C # et je pense 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 pour tout le monde de commencer par des pointeurs, mais l’important 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.