C# Language
Pointeurs
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
}