Programmation C sharp/Code non vérifié
Le code produit par la compilation d'un programme C# est géré par l'environnement .Net qui effectue diverses vérifications, gère la mémoire en utilisant un ramasse-miette et peut lancer des exceptions en cas d'erreur : référence nulle, divisions par zéro, variable non initialisée, indexation d'un tableau au delà de ses limites.
Le langage C# permet d'utiliser du code non vérifié pour utiliser des pointeurs et d'autres fonctionnalités non sécurisées. Ce mode de fonctionnement est donc à utiliser avec précautions, et à éviter pour les développeurs débutants. |
Le recours à du code non vérifié peut être nécessaire pour utiliser le système d'exploitation, un périphérique accédé par adresse mémoire, ...
Le code non vérifié doit obligatoirement être marqué avec le mot-clé unsafe
. Ce qui permet d'empêcher son exécution dans un contexte non sûr (code provenant d'une source non fiable).
Déclaration
[modifier | modifier le wikicode]Le mot-clé unsafe peut être ajouté à la déclaration d'une méthode comme dans l'exemple suivant :
public unsafe void Methode()
{
int iData = 10;
int* pData = &iData;
Console.WriteLine("Data contient " + *pData);
Console.WriteLine("Son adresse est " + (int)pData );
}
Il est également possible de l'ajouter pour un bloc d'instruction seul :
public void Methode()
{
int iData = 10;
unsafe
{
int* pData = &iData;
Console.WriteLine("Data contient " + *pData);
Console.WriteLine("Son adresse est " + (int)pData );
}
}
Pointeurs
[modifier | modifier le wikicode]Un pointeur est un type qui stocke une adresse vers une donnée du type spécifié par le pointeur. La syntaxe d'utilisation est la même que dans les langages C et C++.
Déclaration
[modifier | modifier le wikicode]La déclaration d'un pointeur utilise un type suivi du caractère étoile.
Exemple :
int* pEntier; // Pointeur d'entier
Adresse d'une variable
[modifier | modifier le wikicode]Un pointeur peut recevoir l'adresse d'une variable, en faisant précéder la variable du caractère &
.
Exemple :
int total;
pEntier = &total; // adresse de la variable total
Déréférencer un pointeur
[modifier | modifier le wikicode]Un pointeur utilisé directement donnera l'adresse de la variable.
Pour utiliser le contenu pointé par le pointeur il faut le déréférencer en le faisant précéder du caractère étoile *
.
Exemple :
*pEntier = 100; // modification de la variable total
Membre d'une classe pointée
[modifier | modifier le wikicode]Pour accéder à un membre d'un objet pointé il est possible d'utiliser la syntaxe suivante :
(*pEntier).ToString(); // accès à la méthode ToString de l'entier pointé
Ou d'utiliser l'opérateur flèche équivalent :
pEntier->ToString(); // accès à la méthode ToString de l'entier pointé
Pointeur et tableau
[modifier | modifier le wikicode]L'adresse d'un tableau est donnée sans utiliser l'opérateur d'adresse &
. Toutefois, il n'est pas possible de modifier l'adresse du tableau afin d'éviter de perdre l'adresse de début du tableau. Le pointeur doit utiliser le mot-clé fixed
pour obtenir l'adresse d'un tableau.
Exemple :
int[] valeurs = new int[10];
fixed (int* pEntier = valeurs)
for (int iIndex = 0; iIndex < 10; iIndex++)
Console.WriteLine( *(pEntier + iIndex) );
Gestion de la mémoire
[modifier | modifier le wikicode]Le mode non vérifié permet de modifier le comportement du ramasse-miettes.
Éviter le déplacement par le ramasse-miettes
[modifier | modifier le wikicode]Le mot clé fixed
sert à éviter qu'un tableau ou un objet ne soit déplacé en mémoire par le ramasse-miettes :
- pour empêcher le déplacement durant l'exécution d'une instruction ou un bloc d'instructions :
fixed (type* pointeur = adresse) instruction
- pour déclarer un tableau de taille fixe (non déplacé en mémoire) :
fixed type[nombre] variable;
Exemple :
protected fixed int[12] jours_par_mois;
Taille d'une variable ou d'un type
[modifier | modifier le wikicode]L'opérateur sizeof
s'utilise dans un contexte de code non vérifié, comme une fonction renvoyant le nombre d'octets occupés par la variable ou le type spécifié.
Exemple :
int a;
unsafe
{
System.out.println("Taille de a : "+ sizeof(a) +" octets");
System.out.println("Taille d'un entier : "+ sizeof(int) +" octets");
}
Allocation sur la pile
[modifier | modifier le wikicode]Le mot-clé stackalloc
permet d'allouer un objet ou un tableau sur la pile plutôt que sur le tas. Dans ce cas, cet objet ou tableau n'est pas géré par le ramasse-miettes. Il est donc possible d'utiliser un pointeur sans utiliser le mot-clé fixed
.
Syntaxe : le mot-clé stackalloc
s'utilise à la place du mot-clé new
pour initialiser des pointeurs locaux.
Exemple :
unsafe
{
// allouer 10 entiers sur la pile
int* pEntier = stackalloc int[10];
...
}