Recherche…


Remarques

Pointeurs et unsafe

En raison de leur nature, les pointeurs produisent du code invérifiable. Ainsi, l'utilisation de tout type de pointeur nécessite un contexte unsafe .

Le type System.IntPtr est un wrapper sûr autour d'un void* . Il est conçu comme une alternative plus pratique au void* lorsqu'un contexte dangereux n'est pas requis pour effectuer la tâche en cours.

Comportement non défini

Comme en C et C ++, l'utilisation incorrecte des pointeurs peut invoquer un comportement indéfini, avec des effets secondaires possibles, à savoir la corruption de la mémoire et l'exécution de code non intentionnel. En raison de la nature invérifiable de la plupart des opérations de pointeur, l'utilisation correcte des pointeurs est entièrement à la charge du programmeur.

Types prenant en charge les pointeurs

Contrairement à C et C ++, tous les types C # ne possèdent pas les types de pointeurs correspondants. Un type T peut avoir un type de pointeur correspondant si les deux critères suivants s'appliquent:

  • T est un type de structure ou un type de pointeur.
  • T contient uniquement les membres qui satisfont ces deux critères de manière récursive.

Pointeurs d'accès au tableau

Cet exemple montre comment les pointeurs peuvent être utilisés pour un accès de type C aux tableaux C #.

unsafe
{
    var buffer = new int[1024];
    fixed (int* p = &buffer[0])
    {
        for (var i = 0; i < buffer.Length; i++)
        {
            *(p + i) = i;
        }
    }
}

Le mot-clé unsafe est requis car l'accès au pointeur n'émettra aucune vérification des limites normalement émises lors de l'accès aux tableaux C # de manière régulière.

Le mot-clé fixed indique au compilateur C # d'émettre des instructions pour épingler l'objet d'une manière sûre. L'épinglage est nécessaire pour garantir que le garbage collector ne déplace pas le tableau en mémoire, car cela invaliderait les pointeurs pointant dans le tableau.

Arithmétique de pointeur

L'addition et la soustraction dans les pointeurs fonctionnent différemment des nombres entiers. Lorsqu'un pointeur est incrémenté ou décrémenté, l'adresse indiquée est augmentée ou diminuée de la taille du type de référence.

Par exemple, le type int (alias pour System.Int32 ) a une taille de 4. Si un int peut être stocké dans l'adresse 0, l' int ultérieur peut être stocké dans l'adresse 4, et ainsi de suite. Dans du code:

var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8

De même, le type long (alias pour System.Int64 ) a une taille de 8. Si un long peut être stocké dans l'adresse 0, le long peut être stocké dans l'adresse 8, et ainsi de suite. Dans du code:

var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16

Le type void est spécial et les pointeurs de void sont également spéciaux et ils sont utilisés comme pointeurs fourre-tout lorsque le type n'est pas connu ou n'a pas d'importance. En raison de leur nature agnostique, les pointeurs de void ne peuvent pas être incrémentés ou décrémentés:

var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));

L'astérisque fait partie du type

En C et C ++, l'astérisque dans la déclaration d'une variable de pointeur fait partie de l'expression en cours de déclaration. En C #, l'astérisque dans la déclaration fait partie du type .

En C, C ++ et C #, l'extrait de code suivant déclare un pointeur int :

int* a;

En C et C ++, l'extrait de code suivant déclare un pointeur int et une variable int . En C #, il déclare deux pointeurs int :

int* a, b; 

En C et C ++, l'extrait de code suivant déclare deux pointeurs int . En C #, il est invalide:

int *a, *b;

vide*

C # hérite de C et C ++ l'utilisation de void* tant que pointeur agnostique et agnostique.

void* ptr;

Tout type de pointeur peut être assigné à void* utilisant une conversion implicite:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;

L'inverse nécessite une conversion explicite:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;

Accès membre utilisant ->

C # hérite de C et C ++ l'utilisation du symbole -> comme moyen d'accéder aux membres d'une instance via un pointeur typé.

Considérons la structure suivante:

struct Vector2
{
    public int X;
    public int Y;
}

Voici un exemple d'utilisation de -> pour accéder à ses membres:

Vector2 v;
v.X = 5;
v.Y = 10;

Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();

Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2

Pointeurs génériques

Les critères qu'un type doit satisfaire pour prendre en charge les pointeurs (voir Remarques ) ne peuvent pas être exprimés en termes de contraintes génériques. Par conséquent, toute tentative de déclaration d'un pointeur sur un type fourni via un paramètre de type générique échouera.

void P<T>(T obj) 
    where T : struct
{
    T* ptr = &obj; // compile-time error
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow