Code unsafeUnsafe code

Le langage C# principal, tel que défini dans les chapitres précédents, diffère notamment de C C++ et de l’omission des pointeurs en tant que type de données.The core C# language, as defined in the preceding chapters, differs notably from C and C++ in its omission of pointers as a data type. À la C# place, fournit des références et la capacité de créer des objets gérés par un garbage collector.Instead, C# provides references and the ability to create objects that are managed by a garbage collector. Cette conception, couplée à d’autres fonctionnalités C# , rend un langage plus sûr que C C++ou.This design, coupled with other features, makes C# a much safer language than C or C++. Dans le langage C# principal, il n’est tout simplement pas possible d’avoir une variable non initialisée, un pointeur « non résolu » ou une expression qui indexe un tableau au-delà de ses limites.In the core C# language it is simply not possible to have an uninitialized variable, a "dangling" pointer, or an expression that indexes an array beyond its bounds. Les catégories entières de bogues qui imposent C++ régulièrement le C et les programmes sont donc éliminées.Whole categories of bugs that routinely plague C and C++ programs are thus eliminated.

Bien que presque chaque construction de type pointeur en C++ C ou ait un équivalent de C#type référence dans, néanmoins, il existe des situations où l’accès aux types pointeur devient une nécessité.While practically every pointer type construct in C or C++ has a reference type counterpart in C#, nonetheless, there are situations where access to pointer types becomes a necessity. Par exemple, l’interfaçage avec le système d’exploitation sous-jacent, l’accès à un périphérique mappé en mémoire ou l’implémentation d’un algorithme de temps critique peut ne pas être possible ou pratique sans accès aux pointeurs.For example, interfacing with the underlying operating system, accessing a memory-mapped device, or implementing a time-critical algorithm may not be possible or practical without access to pointers. Pour répondre à ce besoin C# , offre la possibilité d’écrire du code non sécurisé.To address this need, C# provides the ability to write unsafe code.

Dans du code non sécurisé, il est possible de déclarer et d’utiliser des pointeurs, d’effectuer des conversions entre les pointeurs et les types intégraux, de prendre l’adresse des variables, et ainsi de suite.In unsafe code it is possible to declare and operate on pointers, to perform conversions between pointers and integral types, to take the address of variables, and so forth. Dans un sens, l’écriture de code non sécurisé est très similaire à l’écriture C# de code C dans un programme.In a sense, writing unsafe code is much like writing C code within a C# program.

Le code non sécurisé est en fait une fonctionnalité « sécurisée » du point de vue des développeurs et des utilisateurs.Unsafe code is in fact a "safe" feature from the perspective of both developers and users. Le code unsafe doit être clairement marqué avec le modificateur unsafe, de sorte que les développeurs ne peuvent pas utiliser accidentellement des fonctionnalités potentiellement dangereuses, et le moteur d’exécution fonctionne pour s’assurer que le code unsafe ne peut pas être exécuté dans un environnement non fiable.Unsafe code must be clearly marked with the modifier unsafe, so developers can't possibly use unsafe features accidentally, and the execution engine works to ensure that unsafe code cannot be executed in an untrusted environment.

Contextes non sécurisésUnsafe contexts

Les fonctionnalités non sécurisées C# de sont disponibles uniquement dans les contextes non sécurisés.The unsafe features of C# are available only in unsafe contexts. Un contexte unsafe est introduit en incluant un modificateur de unsafe dans la déclaration d’un type ou d’un membre, ou en utilisant un unsafe_statement:An unsafe context is introduced by including an unsafe modifier in the declaration of a type or member, or by employing an unsafe_statement:

  • Une déclaration d’une classe, d’une structure, d’une interface ou d’un délégué peut inclure un modificateur de unsafe, auquel cas la totalité de l’étendue textuelle de cette déclaration de type (y compris le corps de la classe, du struct ou de l’interface) est considérée comme un contexte non sécurisé.A declaration of a class, struct, interface, or delegate may include an unsafe modifier, in which case the entire textual extent of that type declaration (including the body of the class, struct, or interface) is considered an unsafe context.
  • Une déclaration d’un champ, d’une méthode, d’une propriété, d’un événement, d’un indexeur, d’un opérateur, d’un constructeur d’instance, d’un destructeur ou d’un constructeur statique peut inclure un modificateur de unsafe, auquel cas la totalité de l’étendue textuelle de cette déclaration de membre est considérée comme un contexte non sécurisé.A declaration of a field, method, property, event, indexer, operator, instance constructor, destructor, or static constructor may include an unsafe modifier, in which case the entire textual extent of that member declaration is considered an unsafe context.
  • Une unsafe_statement permet l’utilisation d’un contexte unsafe dans un bloc.An unsafe_statement enables the use of an unsafe context within a block. La totalité de l’étendue textuelle du bloc associé est considérée comme un contexte non sécurisé.The entire textual extent of the associated block is considered an unsafe context.

Les productions grammaticales associées sont indiquées ci-dessous.The associated grammar productions are shown below.

class_modifier_unsafe
    : 'unsafe'
    ;

struct_modifier_unsafe
    : 'unsafe'
    ;

interface_modifier_unsafe
    : 'unsafe'
    ;

delegate_modifier_unsafe
    : 'unsafe'
    ;

field_modifier_unsafe
    : 'unsafe'
    ;

method_modifier_unsafe
    : 'unsafe'
    ;

property_modifier_unsafe
    : 'unsafe'
    ;

event_modifier_unsafe
    : 'unsafe'
    ;

indexer_modifier_unsafe
    : 'unsafe'
    ;

operator_modifier_unsafe
    : 'unsafe'
    ;

constructor_modifier_unsafe
    : 'unsafe'
    ;

destructor_declaration_unsafe
    : attributes? 'extern'? 'unsafe'? '~' identifier '(' ')' destructor_body
    | attributes? 'unsafe'? 'extern'? '~' identifier '(' ')' destructor_body
    ;

static_constructor_modifiers_unsafe
    : 'extern'? 'unsafe'? 'static'
    | 'unsafe'? 'extern'? 'static'
    | 'extern'? 'static' 'unsafe'?
    | 'unsafe'? 'static' 'extern'?
    | 'static' 'extern'? 'unsafe'?
    | 'static' 'unsafe'? 'extern'?
    ;

embedded_statement_unsafe
    : unsafe_statement
    | fixed_statement
    ;

unsafe_statement
    : 'unsafe' block
    ;

Dans l’exempleIn the example

public unsafe struct Node
{
    public int Value;
    public Node* Left;
    public Node* Right;
}

le modificateur unsafe spécifié dans la déclaration de struct fait en sorte que la totalité de l’étendue textuelle de la déclaration de struct devienne un contexte non sécurisé.the unsafe modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. Par conséquent, il est possible de déclarer les champs Left et Right comme étant d’un type pointeur.Thus, it is possible to declare the Left and Right fields to be of a pointer type. L’exemple ci-dessus peut également être écritThe example above could also be written

public struct Node
{
    public int Value;
    public unsafe Node* Left;
    public unsafe Node* Right;
}

Ici, les modificateurs unsafe dans les déclarations de champ entraînent la prise en compte de contextes non sécurisés par ces déclarations.Here, the unsafe modifiers in the field declarations cause those declarations to be considered unsafe contexts.

En dehors de l’établissement d’un contexte non sécurisé, ce qui autorise l’utilisation de types pointeur, le modificateur unsafe n’a aucun effet sur un type ou un membre.Other than establishing an unsafe context, thus permitting the use of pointer types, the unsafe modifier has no effect on a type or a member. Dans l’exempleIn the example

public class A
{
    public unsafe virtual void F() {
        char* p;
        ...
    }
}

public class B: A
{
    public override void F() {
        base.F();
        ...
    }
}

le modificateur de unsafe sur la méthode F dans A fait simplement en sorte que l’étendue textuelle de F devienne un contexte non sécurisé dans lequel les fonctionnalités non sécurisées du langage peuvent être utilisées.the unsafe modifier on the F method in A simply causes the textual extent of F to become an unsafe context in which the unsafe features of the language can be used. Dans la substitution de F dans B, il n’est pas nécessaire de spécifier à nouveau le modificateur unsafe, sauf si, bien sûr, la méthode F dans B elle-même a besoin d’accéder à des fonctionnalités non sécurisées.In the override of F in B, there is no need to re-specify the unsafe modifier -- unless, of course, the F method in B itself needs access to unsafe features.

La situation est légèrement différente lorsqu’un type pointeur fait partie de la signature de la méthode.The situation is slightly different when a pointer type is part of the method's signature

public unsafe class A
{
    public virtual void F(char* p) {...}
}

public class B: A
{
    public unsafe override void F(char* p) {...}
}

Ici, étant donné que la signature de Fcomprend un type pointeur, elle peut uniquement être écrite dans un contexte non sécurisé.Here, because F's signature includes a pointer type, it can only be written in an unsafe context. Toutefois, le contexte unsafe peut être introduit soit en rendant la classe entière non sécurisée, comme c’est le cas dans A, soit en incluant un modificateur de unsafe dans la déclaration de méthode, comme c’est le cas dans B.However, the unsafe context can be introduced by either making the entire class unsafe, as is the case in A, or by including an unsafe modifier in the method declaration, as is the case in B.

Types de pointeurPointer types

Dans un contexte unsafe, un type (types) peut être un pointer_type , ainsi qu’un Value_type ou un reference_type.In an unsafe context, a type (Types) may be a pointer_type as well as a value_type or a reference_type. Toutefois, un pointer_type peut également être utilisé dans une expression typeof (expressions de création d’objets anonymes) en dehors d’un contexte non sécurisé, car cette utilisation n’est pas fiable.However, a pointer_type may also be used in a typeof expression (Anonymous object creation expressions) outside of an unsafe context as such usage is not unsafe.

type_unsafe
    : pointer_type
    ;

Un pointer_type est écrit sous la forme d’un unmanaged_type ou du mot clé void, suivi d’un jeton * :A pointer_type is written as an unmanaged_type or the keyword void, followed by a * token:

pointer_type
    : unmanaged_type '*'
    | 'void' '*'
    ;

unmanaged_type
    : type
    ;

Le type spécifié avant le * dans un type pointeur est appelé type référent du type pointeur.The type specified before the * in a pointer type is called the referent type of the pointer type. Il représente le type de la variable vers laquelle la valeur du type pointeur pointe.It represents the type of the variable to which a value of the pointer type points.

Contrairement aux références (valeurs de types référence), les pointeurs ne sont pas suivis par le récupérateur de mémoire : le garbage collector n’a aucune connaissance des pointeurs et des données auxquelles ils pointent.Unlike references (values of reference types), pointers are not tracked by the garbage collector -- the garbage collector has no knowledge of pointers and the data to which they point. Pour cette raison, un pointeur n’est pas autorisé à pointer vers une référence ou un struct qui contient des références, et le type référent d’un pointeur doit être un unmanaged_type.For this reason a pointer is not permitted to point to a reference or to a struct that contains references, and the referent type of a pointer must be an unmanaged_type.

Un unmanaged_type est un type qui n’est pas un type reference_type ou construit, et ne contient pas de champs de type reference_type ou construits à aucun niveau d’imbrication.An unmanaged_type is any type that isn't a reference_type or constructed type, and doesn't contain reference_type or constructed type fields at any level of nesting. En d’autres termes, un unmanaged_type est l’un des éléments suivants :In other words, an unmanaged_type is one of the following:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimalou bool.sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Tout enum_type.Any enum_type.
  • Tout pointer_type.Any pointer_type.
  • Tout struct_type défini par l’utilisateur qui n’est pas un type construit et contient uniquement des champs de unmanaged_types.Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.

La règle intuitive pour mélanger des pointeurs et des références est que les référents de références (objets) sont autorisés à contenir des pointeurs, mais les référents de pointeurs ne sont pas autorisés à contenir des références.The intuitive rule for mixing of pointers and references is that referents of references (objects) are permitted to contain pointers, but referents of pointers are not permitted to contain references.

Des exemples de types pointeur sont fournis dans le tableau ci-dessous :Some examples of pointer types are given in the table below:

ExempleExample DescriptionDescription
byte* Pointeur vers bytePointer to byte
char* Pointeur vers charPointer to char
int** Pointeur vers un pointeur vers intPointer to pointer to int
int*[] Tableau unidimensionnel de pointeurs vers intSingle-dimensional array of pointers to int
void* Pointeur vers un type inconnuPointer to unknown type

Pour une implémentation donnée, tous les types de pointeurs doivent avoir la même taille et la même représentation.For a given implementation, all pointer types must have the same size and representation.

Contrairement à C C++et, lorsque plusieurs pointeurs sont déclarés dans la même déclaration C# , le * est écrit avec le type sous-jacent uniquement, et non comme un signe de ponctuation de préfixe sur chaque nom de pointeur.Unlike C and C++, when multiple pointers are declared in the same declaration, in C# the * is written along with the underlying type only, not as a prefix punctuator on each pointer name. Exemple :For example

int* pi, pj;    // NOT as int *pi, *pj;

La valeur d’un pointeur de type T* représente l’adresse d’une variable de type T.The value of a pointer having type T* represents the address of a variable of type T. L’opérateur d’indirection de pointeur * (indirection de pointeur) peut être utilisé pour accéder à cette variable.The pointer indirection operator * (Pointer indirection) may be used to access this variable. Par exemple, à partir d’une variable P de type int*, l’expression *P indique la variable int trouvée à l’adresse contenue dans P.For example, given a variable P of type int*, the expression *P denotes the int variable found at the address contained in P.

À l’instar d’une référence d’objet, un pointeur peut être null.Like an object reference, a pointer may be null. L’application de l’opérateur d’indirection à un pointeur null entraîne un comportement défini par l’implémentation.Applying the indirection operator to a null pointer results in implementation-defined behavior. Un pointeur avec une valeur null est représenté par All-bits-Zero.A pointer with value null is represented by all-bits-zero.

Le type de void* représente un pointeur vers un type inconnu.The void* type represents a pointer to an unknown type. Étant donné que le type référent est inconnu, l’opérateur d’indirection ne peut pas être appliqué à un pointeur de type void*, et aucun arithmétique n’est exécuté sur ce pointeur.Because the referent type is unknown, the indirection operator cannot be applied to a pointer of type void*, nor can any arithmetic be performed on such a pointer. Toutefois, un pointeur de type void* peut être casté en n’importe quel autre type pointeur (et inversement).However, a pointer of type void* can be cast to any other pointer type (and vice versa).

Les types pointeur sont une catégorie distincte de types.Pointer types are a separate category of types. Contrairement aux types référence et aux types valeur, les types pointeur n’héritent pas de object et aucune conversion n’existe entre les types pointeur et les object.Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object. En particulier, le boxing et l’unboxing (boxing et unboxing) ne sont pas pris en charge pour les pointeurs.In particular, boxing and unboxing (Boxing and unboxing) are not supported for pointers. Toutefois, les conversions sont autorisées entre les différents types pointeur et entre les types pointeur et les types intégraux.However, conversions are permitted between different pointer types and between pointer types and the integral types. Cela est décrit dans conversions de pointeurs.This is described in Pointer conversions.

Un pointer_type ne peut pas être utilisé en tant qu’argument de type (types construits), et l’inférence de type (inférence de type) échoue sur les appels de méthode génériques qui auraient pu déduire un argument de type comme étant un type pointeur.A pointer_type cannot be used as a type argument (Constructed types), and type inference (Type inference) fails on generic method calls that would have inferred a type argument to be a pointer type.

Un pointer_type peut être utilisé comme type de champ volatile (champs volatiles).A pointer_type may be used as the type of a volatile field (Volatile fields).

Bien que les pointeurs puissent être passés comme des ref ou des paramètres de out, cela peut entraîner un comportement indéfini, puisque le pointeur peut être défini de manière à pointer vers une variable locale qui n’existe plus quand la méthode appelée est retournée, ou l’objet fixe auquel il est utilisé pour pointer, n’est plus résolu.Although pointers can be passed as ref or out parameters, doing so can cause undefined behavior, since the pointer may well be set to point to a local variable which no longer exists when the called method returns, or the fixed object to which it used to point, is no longer fixed. Exemple :For example:

using System;

class Test
{
    static int value = 20;

    unsafe static void F(out int* pi1, ref int* pi2) {
        int i = 10;
        pi1 = &i;

        fixed (int* pj = &value) {
            // ...
            pi2 = pj;
        }
    }

    static void Main() {
        int i = 10;
        unsafe {
            int* px1;
            int* px2 = &i;

            F(out px1, ref px2);

            Console.WriteLine("*px1 = {0}, *px2 = {1}",
                *px1, *px2);    // undefined behavior
        }
    }
}

Une méthode peut retourner une valeur d’un certain type et ce type peut être un pointeur.A method can return a value of some type, and that type can be a pointer. Par exemple, lorsqu’un pointeur désignant une séquence contiguë de ints, le nombre d’éléments de cette séquence et une autre valeur int, la méthode suivante retourne l’adresse de cette valeur dans cette séquence, si une correspondance est trouvée ; Sinon, elle retourne null:For example, when given a pointer to a contiguous sequence of ints, that sequence's element count, and some other int value, the following method returns the address of that value in that sequence, if a match occurs; otherwise it returns null:

unsafe static int* Find(int* pi, int size, int value) {
    for (int i = 0; i < size; ++i) {
        if (*pi == value) 
            return pi;
        ++pi;
    }
    return null;
}

Dans un contexte non sécurisé, plusieurs constructions sont disponibles pour l’utilisation des pointeurs :In an unsafe context, several constructs are available for operating on pointers:

Variables fixes et déplaçablesFixed and moveable variables

L’opérateur address-of (l’opérateur address-of) et l’instruction fixed (l’instruction fixed) divisent les variables en deux catégories : les variables fixes et les variables déplaçables.The address-of operator (The address-of operator) and the fixed statement (The fixed statement) divide variables into two categories: Fixed variables and moveable variables.

Les variables fixes résident dans des emplacements de stockage qui ne sont pas affectés par l’opération du garbage collector.Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. (Les variables locales, les paramètres de valeur et les variables créées par des pointeurs de déréférencement sont des exemples de variables fixes.) En revanche, les variables déplaçables résident dans des emplacements de stockage qui peuvent faire l’objet d’un déplacement ou d’une suppression par le garbage collector.(Examples of fixed variables include local variables, value parameters, and variables created by dereferencing pointers.) On the other hand, moveable variables reside in storage locations that are subject to relocation or disposal by the garbage collector. (Les exemples de variables déplaçables incluent des champs dans des objets et des éléments de tableaux.)(Examples of moveable variables include fields in objects and elements of arrays.)

L’opérateur & (opérateur d’adresse) autorise l’obtention de l’adresse d’une variable fixe sans restrictions.The & operator (The address-of operator) permits the address of a fixed variable to be obtained without restrictions. Toutefois, étant donné qu’une variable déplaçable est sujette à un déplacement ou une suppression par le garbage collector, l’adresse d’une variable déplaçable peut uniquement être obtenue à l’aide d’une instruction fixed (l’instruction fixed) et cette adresse reste valide uniquement pour la durée de cette instruction fixed.However, because a moveable variable is subject to relocation or disposal by the garbage collector, the address of a moveable variable can only be obtained using a fixed statement (The fixed statement), and that address remains valid only for the duration of that fixed statement.

En termes précis, une variable fixe est l’un des éléments suivants :In precise terms, a fixed variable is one of the following:

  • Variable résultant d’un simple_name (noms simples) qui fait référence à une variable locale ou à un paramètre de valeur, sauf si la variable est capturée par une fonction anonyme.A variable resulting from a simple_name (Simple names) that refers to a local variable or a value parameter, unless the variable is captured by an anonymous function.
  • Variable résultant d’un member_access (accès aux membres) de la forme V.I, où V est une variable fixe d’un struct_type.A variable resulting from a member_access (Member access) of the form V.I, where V is a fixed variable of a struct_type.
  • Une variable résultant d’une pointer_indirection_expression (indirection de pointeur) de la forme *P, une pointer_member_access (accès aux membres du pointeur) de la P->Ide formulaire ou un pointer_element_access (accès à un élément pointeur) de la P[E]de formulaire.A variable resulting from a pointer_indirection_expression (Pointer indirection) of the form *P, a pointer_member_access (Pointer member access) of the form P->I, or a pointer_element_access (Pointer element access) of the form P[E].

Toutes les autres variables sont classées comme variables déplaçables.All other variables are classified as moveable variables.

Notez qu’un champ statique est classé comme une variable déplaçable.Note that a static field is classified as a moveable variable. Notez également qu’un paramètre ref ou out est classé comme une variable déplaçable, même si l’argument donné pour le paramètre est une variable fixe.Also note that a ref or out parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. Enfin, Notez qu’une variable produite par le déréférencement d’un pointeur est toujours classifiée comme une variable fixe.Finally, note that a variable produced by dereferencing a pointer is always classified as a fixed variable.

Conversions de pointeursPointer conversions

Dans un contexte non sécurisé, l’ensemble des conversions implicites disponibles (conversions implicites) est étendu pour inclure les conversions de pointeur implicites suivantes :In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • À partir de n’importe quel pointer_type vers le type void*.From any pointer_type to the type void*.
  • Du littéral null à n’importe quel pointer_type.From the null literal to any pointer_type.

En outre, dans un contexte non sécurisé, l’ensemble des conversions explicites disponibles (conversions explicites) est étendu pour inclure les conversions de pointeur explicites suivantes :Additionally, in an unsafe context, the set of available explicit conversions (Explicit conversions) is extended to include the following explicit pointer conversions:

  • De tout pointer_type à n’importe quel autre pointer_type.From any pointer_type to any other pointer_type.
  • À partir de sbyte, byte, short, ushort, int, uint, longou ulong à n’importe quel pointer_type.From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer_type.
  • À partir de n’importe quelle pointer_type vers sbyte, byte, short, ushort, int, uint, longou ulong.From any pointer_type to sbyte, byte, short, ushort, int, uint, long, or ulong.

Enfin, dans un contexte non sécurisé, l’ensemble de conversions implicites standard (conversions implicites standard) comprend la conversion de pointeur suivante :Finally, in an unsafe context, the set of standard implicit conversions (Standard implicit conversions) includes the following pointer conversion:

  • À partir de n’importe quel pointer_type vers le type void*.From any pointer_type to the type void*.

Les conversions entre deux types pointeur ne modifient jamais la valeur du pointeur réel.Conversions between two pointer types never change the actual pointer value. En d’autres termes, une conversion d’un type pointeur vers un autre n’a aucun effet sur l’adresse sous-jacente donnée par le pointeur.In other words, a conversion from one pointer type to another has no effect on the underlying address given by the pointer.

Quand un type pointeur est converti en un autre, si le pointeur résultant n’est pas correctement aligné pour le type pointé, le comportement n’est pas défini si le résultat est déréférencé.When one pointer type is converted to another, if the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined if the result is dereferenced. En général, le concept « aligné correctement » est transitif : si un pointeur vers le type A est correctement aligné pour un pointeur vers le type B, qui, à son tour, est correctement aligné pour un pointeur vers le type C, un pointeur vers le type A est correctement aligné pour un pointeur vers le type C.In general, the concept "correctly aligned" is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which, in turn, is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.

Considérez le cas suivant dans lequel une variable ayant un type est accessible via un pointeur vers un type différent :Consider the following case in which a variable having one type is accessed via a pointer to a different type:

char c = 'A';
char* pc = &c;
void* pv = pc;
int* pi = (int*)pv;
int i = *pi;         // undefined
*pi = 123456;        // undefined

Quand un type pointeur est converti en un pointeur vers un octet, le résultat pointe vers l’octet adressé le plus bas de la variable.When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the variable. Les incréments successifs du résultat, jusqu’à la taille de la variable, produisent des pointeurs vers les octets restants de cette variable.Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. Par exemple, la méthode suivante affiche chacun des huit octets d’une valeur double sous la forme d’une valeur hexadécimale :For example, the following method displays each of the eight bytes in a double as a hexadecimal value:

using System;

class Test
{
    unsafe static void Main() {
      double d = 123.456e23;
        unsafe {
           byte* pb = (byte*)&d;
            for (int i = 0; i < sizeof(double); ++i)
               Console.Write("{0:X2} ", *pb++);
            Console.WriteLine();
        }
    }
}

Bien entendu, la sortie générée dépend de endianness.Of course, the output produced depends on endianness.

Les mappages entre les pointeurs et les entiers sont définis par l’implémentation.Mappings between pointers and integers are implementation-defined. Toutefois, sur les architectures d’UC 32 * et 64 bits avec un espace d’adressage linéaire, les conversions de pointeurs vers ou depuis les types intégraux se comportent généralement exactement comme des conversions de valeurs uint ou ulong, respectivement, vers ou à partir de ces types intégraux.However, on 32* and 64-bit CPU architectures with a linear address space, conversions of pointers to or from integral types typically behave exactly like conversions of uint or ulong values, respectively, to or from those integral types.

Tableaux de pointeursPointer arrays

Dans un contexte non sécurisé, des tableaux de pointeurs peuvent être construits.In an unsafe context, arrays of pointers can be constructed. Seules certaines des conversions qui s’appliquent à d’autres types de tableau sont autorisées sur les tableaux de pointeurs :Only some of the conversions that apply to other array types are allowed on pointer arrays:

  • La conversion de référence implicite (conversions de référence implicites) de tout array_type en System.Array et les interfaces qu’elle implémente s’appliquent également aux tableaux de pointeurs.The implicit reference conversion (Implicit reference conversions) from any array_type to System.Array and the interfaces it implements also applies to pointer arrays. Toutefois, toute tentative d’accès aux éléments du tableau par le biais de System.Array ou des interfaces qu’il implémente entraîne une exception au moment de l’exécution, car les types de pointeurs ne sont pas convertibles en object.However, any attempt to access the array elements through System.Array or the interfaces it implements will result in an exception at run-time, as pointer types are not convertible to object.
  • Les conversions de référence implicite et explicite (conversions deréférence implicites, conversions de référence explicites) d’un type de tableau unidimensionnel S[] en System.Collections.Generic.IList<T> et ses interfaces de base génériques ne s’appliquent jamais aux tableaux de pointeurs, car les types pointeur ne peuvent pas être utilisés en tant qu’arguments de type, et il n’y a aucune conversion des types pointeurThe implicit and explicit reference conversions (Implicit reference conversions, Explicit reference conversions) from a single-dimensional array type S[] to System.Collections.Generic.IList<T> and its generic base interfaces never apply to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types.
  • La conversion de référence explicite (conversions de références explicites) à partir de System.Array et les interfaces qu’elle implémente sur n’importe quel array_type s’applique aux tableaux de pointeurs.The explicit reference conversion (Explicit reference conversions) from System.Array and the interfaces it implements to any array_type applies to pointer arrays.
  • Les conversions de référence explicites (conversions de référence explicites) de System.Collections.Generic.IList<S> et ses interfaces de base en un type tableau unidimensionnel T[] ne s’appliquent jamais aux tableaux de pointeurs, puisque les types pointeur ne peuvent pas être utilisés comme arguments de type et qu’il n’y a pas de conversion des types pointeur en types non-pointeur.The explicit reference conversions (Explicit reference conversions) from System.Collections.Generic.IList<S> and its base interfaces to a single-dimensional array type T[] never applies to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types.

Ces restrictions signifient que l’expansion de l’instruction foreach sur les tableaux décrits dans l’instruction foreach ne peut pas être appliquée aux tableaux de pointeurs.These restrictions mean that the expansion for the foreach statement over arrays described in The foreach statement cannot be applied to pointer arrays. Au lieu de cela, une instruction foreach de la formeInstead, a foreach statement of the form

foreach (V v in x) embedded_statement

où le type de x est un type de tableau de la forme T[,,...,], N est le nombre de dimensions moins 1 et T ou V est un type pointeur, est développé à l’aide de boucles for imbriquées comme suit :where the type of x is an array type of the form T[,,...,], N is the number of dimensions minus 1 and T or V is a pointer type, is expanded using nested for-loops as follows:

{
    T[,,...,] a = x;
    for (int i0 = a.GetLowerBound(0); i0 <= a.GetUpperBound(0); i0++)
    for (int i1 = a.GetLowerBound(1); i1 <= a.GetUpperBound(1); i1++)
    ...
    for (int iN = a.GetLowerBound(N); iN <= a.GetUpperBound(N); iN++) {
        V v = (V)a.GetValue(i0,i1,...,iN);
        embedded_statement
    }
}

Les variables a, i0, i1,..., iN ne sont pas visibles ou accessibles à x ou au embedded_statement ou à tout autre code source du programme.The variables a, i0, i1, ..., iN are not visible to or accessible to x or the embedded_statement or any other source code of the program. La variable v est en lecture seule dans l’instruction incorporée.The variable v is read-only in the embedded statement. S’il n’existe pas de conversion explicite (conversions de pointeur) de T (type d’élément) en V, une erreur est générée et aucune autre étape n’est effectuée.If there is not an explicit conversion (Pointer conversions) from T (the element type) to V, an error is produced and no further steps are taken. Si x a la valeur null, une System.NullReferenceException est levée au moment de l’exécution.If x has the value null, a System.NullReferenceException is thrown at run-time.

Pointeurs dans les expressionsPointers in expressions

Dans un contexte unsafe, une expression peut produire un résultat d’un type pointeur, mais en dehors d’un contexte unsafe, il s’agit d’une erreur de compilation pour qu’une expression soit d’un type pointeur.In an unsafe context, an expression may yield a result of a pointer type, but outside an unsafe context it is a compile-time error for an expression to be of a pointer type. En termes précis, en dehors d’un contexte non sécurisé, une erreur de compilation se produit si une simple_name (noms simples), member_access (accès aux membres), invocation_expression (expressions d’appel) ou element_access (accès à l’élément) est d’un type pointeur.In precise terms, outside an unsafe context a compile-time error occurs if any simple_name (Simple names), member_access (Member access), invocation_expression (Invocation expressions), or element_access (Element access) is of a pointer type.

Dans un contexte non sécurisé, les productions primary_no_array_creation_expression (expressions primaires) et unary_expression (opérateurs unaires) autorisent les constructions supplémentaires suivantes :In an unsafe context, the primary_no_array_creation_expression (Primary expressions) and unary_expression (Unary operators) productions permit the following additional constructs:

primary_no_array_creation_expression_unsafe
    : pointer_member_access
    | pointer_element_access
    | sizeof_expression
    ;

unary_expression_unsafe
    : pointer_indirection_expression
    | addressof_expression
    ;

Ces constructions sont décrites dans les sections suivantes.These constructs are described in the following sections. La priorité et l’associativité des opérateurs non sécurisés sont implicites par la grammaire.The precedence and associativity of the unsafe operators is implied by the grammar.

Indirection de pointeurPointer indirection

Un pointer_indirection_expression se compose d’un astérisque (*) suivi d’un unary_expression.A pointer_indirection_expression consists of an asterisk (*) followed by a unary_expression.

pointer_indirection_expression
    : '*' unary_expression
    ;

L’opérateur de * unaire désigne l’indirection de pointeur et est utilisé pour obtenir la variable vers laquelle pointe un pointeur.The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. Le résultat de l’évaluation de *P, où P est une expression d’un type pointeur T*, est une variable de type T.The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. Il s’agit d’une erreur au moment de la compilation pour appliquer l’opérateur unaire * à une expression de type void* ou à une expression qui n’est pas d’un type pointeur.It is a compile-time error to apply the unary * operator to an expression of type void* or to an expression that isn't of a pointer type.

L’effet de l’application de l’opérateur de * unaire à un pointeur null est défini par l’implémentation.The effect of applying the unary * operator to a null pointer is implementation-defined. En particulier, il n’y a aucune garantie que cette opération lève une System.NullReferenceException.In particular, there is no guarantee that this operation throws a System.NullReferenceException.

Si une valeur non valide a été assignée au pointeur, le comportement de l’opérateur d' * unaire n’est pas défini.If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. Parmi les valeurs non valides pour le déréférencement d’un pointeur par l’opérateur unaire * sont une adresse alignée de manière inappropriée pour le type pointé (Voir l’exemple dans les conversions de pointeur) et l’adresse d’une variable après la fin de sa durée de vie.Among the invalid values for dereferencing a pointer by the unary * operator are an address inappropriately aligned for the type pointed to (see example in Pointer conversions), and the address of a variable after the end of its lifetime.

À des fins d’analyse d’assignation définie, une variable produite par l’évaluation d’une expression de la forme *P est considérée comme initialement assignée (variables initialement affectées).For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned (Initially assigned variables).

Accès au membre pointeurPointer member access

Un pointer_member_access se compose d’un primary_expression, suivi d’un jeton «->», suivi d’un identificateur et d’un type_argument_listfacultatif.A pointer_member_access consists of a primary_expression, followed by a "->" token, followed by an identifier and an optional type_argument_list.

pointer_member_access
    : primary_expression '->' identifier
    ;

Dans un accès de membre de pointeur de la P->Ide formulaire, P doit être une expression d’un type pointeur autre que void*et I doit désigner un membre accessible du type auquel P pointe.In a pointer member access of the form P->I, P must be an expression of a pointer type other than void*, and I must denote an accessible member of the type to which P points.

Un accès de membre de pointeur de la P->I de formulaire est évalué exactement comme (*P).I.A pointer member access of the form P->I is evaluated exactly as (*P).I. Pour obtenir une description de l’opérateur d’indirection de pointeur (*), consultez indirection de pointeur.For a description of the pointer indirection operator (*), see Pointer indirection. Pour obtenir une description de l’opérateur d’accès aux membres (.), consultez accès aux membres.For a description of the member access operator (.), see Member access.

Dans l’exempleIn the example

using System;

struct Point
{
    public int x;
    public int y;

    public override string ToString() {
        return "(" + x + "," + y + ")";
    }
}

class Test
{
    static void Main() {
        Point point;
        unsafe {
            Point* p = &point;
            p->x = 10;
            p->y = 20;
            Console.WriteLine(p->ToString());
        }
    }
}

l’opérateur -> est utilisé pour accéder aux champs et appeler une méthode d’un struct via un pointeur.the -> operator is used to access fields and invoke a method of a struct through a pointer. Étant donné que l’opération P->I équivaut précisément à (*P).I, la méthode Main peut également être écrite :Because the operation P->I is precisely equivalent to (*P).I, the Main method could equally well have been written:

class Test
{
    static void Main() {
        Point point;
        unsafe {
            Point* p = &point;
            (*p).x = 10;
            (*p).y = 20;
            Console.WriteLine((*p).ToString());
        }
    }
}

Accès à l’élément pointeurPointer element access

Une pointer_element_access se compose d’une primary_no_array_creation_expression suivie d’une expression placée entre «[» et «]».A pointer_element_access consists of a primary_no_array_creation_expression followed by an expression enclosed in "[" and "]".

pointer_element_access
    : primary_no_array_creation_expression '[' expression ']'
    ;

Dans un accès à un élément de pointeur de la P[E]de formulaire, P doit être une expression d’un type pointeur autre que void*et E doit être une expression qui peut être convertie implicitement en int, uint, longou ulong.In a pointer element access of the form P[E], P must be an expression of a pointer type other than void*, and E must be an expression that can be implicitly converted to int, uint, long, or ulong.

Un accès à un élément de pointeur de la P[E] de formulaire est évalué exactement comme *(P + E).A pointer element access of the form P[E] is evaluated exactly as *(P + E). Pour obtenir une description de l’opérateur d’indirection de pointeur (*), consultez indirection de pointeur.For a description of the pointer indirection operator (*), see Pointer indirection. Pour obtenir une description de l’opérateur d’addition de pointeurs (+), consultez opérations arithmétiques sur les pointeurs.For a description of the pointer addition operator (+), see Pointer arithmetic.

Dans l’exempleIn the example

class Test
{
    static void Main() {
        unsafe {
            char* p = stackalloc char[256];
            for (int i = 0; i < 256; i++) p[i] = (char)i;
        }
    }
}

un accès à un élément pointeur est utilisé pour initialiser la mémoire tampon de caractères dans une boucle for.a pointer element access is used to initialize the character buffer in a for loop. Étant donné que l’opération P[E] équivaut précisément à *(P + E), l’exemple peut également être écrit :Because the operation P[E] is precisely equivalent to *(P + E), the example could equally well have been written:

class Test
{
    static void Main() {
        unsafe {
            char* p = stackalloc char[256];
            for (int i = 0; i < 256; i++) *(p + i) = (char)i;
        }
    }
}

L’opérateur d’accès à l’élément pointeur ne vérifie pas les erreurs de limites hors limites et le comportement lors de l’accès à un élément hors limites n’est pas défini.The pointer element access operator does not check for out-of-bounds errors and the behavior when accessing an out-of-bounds element is undefined. Il s’agit du même que C C++et.This is the same as C and C++.

Opérateur d’adresseThe address-of operator

Une addressof_expression se compose d’une esperluette (&) suivie d’un unary_expression.An addressof_expression consists of an ampersand (&) followed by a unary_expression.

addressof_expression
    : '&' unary_expression
    ;

Dans le cas d’une expression E qui est de type T et qui est classée en tant que variable fixe (variables fixes et déplaçables), la construction &E calcule l’adresse de la variable fournie par E.Given an expression E which is of a type T and is classified as a fixed variable (Fixed and moveable variables), the construct &E computes the address of the variable given by E. Le type du résultat est T* et est classifié comme une valeur.The type of the result is T* and is classified as a value. Une erreur de compilation se produit si E n’est pas classée en tant que variable, si E est classée en tant que variable locale en lecture seule, ou si E désigne une variable déplaçable.A compile-time error occurs if E is not classified as a variable, if E is classified as a read-only local variable, or if E denotes a moveable variable. Dans le dernier cas, une instruction fixed (l’instruction fixed) peut être utilisée pour « corriger » temporairement la variable avant d’obtenir son adresse.In the last case, a fixed statement (The fixed statement) can be used to temporarily "fix" the variable before obtaining its address. Comme indiqué dans l’accès aux membres, en dehors d’un constructeur d’instance ou d’un constructeur statique pour un struct ou une classe qui définit un champ readonly, ce champ est considéré comme une valeur, et non comme une variable.As stated in Member access, outside an instance constructor or static constructor for a struct or class that defines a readonly field, that field is considered a value, not a variable. Par conséquent, son adresse ne peut pas être prise.As such, its address cannot be taken. De même, l’adresse d’une constante ne peut pas être prise.Similarly, the address of a constant cannot be taken.

L’opérateur & ne requiert pas l’assignation définitive de son argument, mais après une opération &, la variable à laquelle l’opérateur est appliqué est considérée comme assignée de manière définitive dans le chemin d’exécution dans lequel l’opération se produit.The & operator does not require its argument to be definitely assigned, but following an & operation, the variable to which the operator is applied is considered definitely assigned in the execution path in which the operation occurs. Il incombe au programmeur de s’assurer que l’initialisation correcte de la variable a réellement lieu dans cette situation.It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

Dans l’exempleIn the example

using System;

class Test
{
    static void Main() {
        int i;
        unsafe {
            int* p = &i;
            *p = 123;
        }
        Console.WriteLine(i);
    }
}

i est considéré comme définitivement affecté à la suite de l’opération &i utilisée pour initialiser p.i is considered definitely assigned following the &i operation used to initialize p. L’assignation à *p en vigueur Initialise i, mais l’inclusion de cette initialisation est la responsabilité du programmeur, et aucune erreur de compilation ne se produit si l’assignation a été supprimée.The assignment to *p in effect initializes i, but the inclusion of this initialization is the responsibility of the programmer, and no compile-time error would occur if the assignment was removed.

Les règles d’attribution définie pour l’opérateur & existent, de sorte que l’initialisation redondante des variables locales peut être évitée.The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. Par exemple, de nombreuses API externes prennent un pointeur vers une structure qui est remplie par l’API.For example, many external APIs take a pointer to a structure which is filled in by the API. Les appels à ces API passent généralement l’adresse d’une variable de struct locale et sans la règle, l’initialisation redondante de la variable de struct est nécessaire.Calls to such APIs typically pass the address of a local struct variable, and without the rule, redundant initialization of the struct variable would be required.

Incrémenter et décrémenter des pointeursPointer increment and decrement

Dans un contexte non sécurisé, les opérateurs ++ et -- (opérateurs suffixés d’incrémentation et de décrémentation et opérateurs d’incrémentation et de décrémentation de préfixe) peuvent être appliqués aux variables pointeur de tous les types, à l’exception des void*.In an unsafe context, the ++ and -- operators (Postfix increment and decrement operators and Prefix increment and decrement operators) can be applied to pointer variables of all types except void*. Ainsi, pour chaque type de pointeur T*, les opérateurs suivants sont définis implicitement :Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator ++(T* x);
T* operator --(T* x);

Les opérateurs produisent les mêmes résultats que x + 1 et x - 1, respectivement (arithmétique de pointeur).The operators produce the same results as x + 1 and x - 1, respectively (Pointer arithmetic). En d’autres termes, pour une variable pointeur de type T*, l’opérateur ++ ajoute sizeof(T) à l’adresse contenue dans la variable, et l’opérateur -- soustrait sizeof(T) de l’adresse contenue dans la variable.In other words, for a pointer variable of type T*, the ++ operator adds sizeof(T) to the address contained in the variable, and the -- operator subtracts sizeof(T) from the address contained in the variable.

Si une opération d’incrémentation ou de décrémentation de pointeur dépasse le domaine du type pointeur, le résultat est défini par l’implémentation, mais aucune exception n’est générée.If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

Arithmétique des pointeursPointer arithmetic

Dans un contexte non sécurisé, les opérateurs + et - (opérateur d’addition et opérateur de soustraction) peuvent être appliqués aux valeurs de tous les types pointeur, à l’exception de void*.In an unsafe context, the + and - operators (Addition operator and Subtraction operator) can be applied to values of all pointer types except void*. Ainsi, pour chaque type de pointeur T*, les opérateurs suivants sont définis implicitement :Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator +(T* x, int y);
T* operator +(T* x, uint y);
T* operator +(T* x, long y);
T* operator +(T* x, ulong y);

T* operator +(int x, T* y);
T* operator +(uint x, T* y);
T* operator +(long x, T* y);
T* operator +(ulong x, T* y);

T* operator -(T* x, int y);
T* operator -(T* x, uint y);
T* operator -(T* x, long y);
T* operator -(T* x, ulong y);

long operator -(T* x, T* y);

À partir d’une expression P d’un type pointeur T* et d’une expression N de type int, uint, longou ulong, les expressions P + N et N + P calculent la valeur de pointeur de type T* qui résulte de l’ajout d' N * sizeof(T) à l’adresse fournie par P.Given an expression P of a pointer type T* and an expression N of type int, uint, long, or ulong, the expressions P + N and N + P compute the pointer value of type T* that results from adding N * sizeof(T) to the address given by P. De même, l’expression P - N calcule la valeur de pointeur de type T* qui résulte de la soustraction des N * sizeof(T) de l’adresse donnée par P.Likewise, the expression P - N computes the pointer value of type T* that results from subtracting N * sizeof(T) from the address given by P.

Étant donné deux expressions, P et Q, d’un type pointeur T*, l’expression P - Q calcule la différence entre les adresses fournies par P et Q, puis divise cette différence par sizeof(T).Given two expressions, P and Q, of a pointer type T*, the expression P - Q computes the difference between the addresses given by P and Q and then divides that difference by sizeof(T). Le type du résultat est toujours long.The type of the result is always long. En effet, P - Q est calculé comme ((long)(P) - (long)(Q)) / sizeof(T).In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

Exemple :For example:

using System;

class Test
{
    static void Main() {
        unsafe {
            int* values = stackalloc int[20];
            int* p = &values[1];
            int* q = &values[15];
            Console.WriteLine("p - q = {0}", p - q);
            Console.WriteLine("q - p = {0}", q - p);
        }
    }
}

ce qui génère la sortie :which produces the output:

p - q = -14
q - p = 14

Si une opération arithmétique de pointeur dépasse le domaine du type pointeur, le résultat est tronqué en mode d’implémentation défini, mais aucune exception n’est générée.If a pointer arithmetic operation overflows the domain of the pointer type, the result is truncated in an implementation-defined fashion, but no exceptions are produced.

Comparaison de pointeursPointer comparison

Dans un contexte non sécurisé, les opérateurs ==, !=, <, >, <=et => (opérateurs relationnels et de test de type) peuvent être appliqués aux valeurs de tous les types pointeur.In an unsafe context, the ==, !=, <, >, <=, and => operators (Relational and type-testing operators) can be applied to values of all pointer types. Les opérateurs de comparaison de pointeurs sont les suivants :The pointer comparison operators are:

bool operator ==(void* x, void* y);
bool operator !=(void* x, void* y);
bool operator <(void* x, void* y);
bool operator >(void* x, void* y);
bool operator <=(void* x, void* y);
bool operator >=(void* x, void* y);

Étant donné qu’une conversion implicite existe de tout type pointeur vers le type void*, les opérandes de tout type pointeur peuvent être comparés à l’aide de ces opérateurs.Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. Les opérateurs de comparaison comparent les adresses données par les deux opérandes comme s’il s’agissait d’entiers non signés.The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

Opérateur sizeofThe sizeof operator

L’opérateur sizeof retourne le nombre d’octets occupés par une variable d’un type donné.The sizeof operator returns the number of bytes occupied by a variable of a given type. Le type spécifié comme opérande de sizeof doit être un unmanaged_type (types pointeur).The type specified as an operand to sizeof must be an unmanaged_type (Pointer types).

sizeof_expression
    : 'sizeof' '(' unmanaged_type ')'
    ;

Le résultat de l’opérateur sizeof est une valeur de type int.The result of the sizeof operator is a value of type int. Pour certains types prédéfinis, l’opérateur sizeof produit une valeur constante, comme indiqué dans le tableau ci-dessous.For certain predefined types, the sizeof operator yields a constant value as shown in the table below.

ExpressionExpression RésultatResult
sizeof(sbyte) 1
sizeof(byte) 1
sizeof(short) 2
sizeof(ushort) 2
sizeof(int) 4
sizeof(uint) 4
sizeof(long) 8
sizeof(ulong) 8
sizeof(char) 2
sizeof(float) 4
sizeof(double) 8
sizeof(bool) 1

Pour tous les autres types, le résultat de l’opérateur sizeof est défini par l’implémentation et est classifié comme une valeur, et non comme une constante.For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

L’ordre dans lequel les membres sont empaquetés dans un struct n’est pas spécifié.The order in which members are packed into a struct is unspecified.

À des fins d’alignement, il peut y avoir un remplissage sans nom au début d’un struct, dans un struct et à la fin du struct.For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. Le contenu des bits utilisés comme remplissage est indéterminé.The contents of the bits used as padding are indeterminate.

En cas d’application à un opérande qui a le type struct, le résultat est le nombre total d’octets dans une variable de ce type, y compris tout remplissage.When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding.

Instruction fixedThe fixed statement

Dans un contexte non sécurisé, la embedded_statement (instructions) production autorise une construction supplémentaire, l’instruction fixed, qui est utilisée pour « corriger » une variable déplaçable de telle sorte que son adresse reste constante pendant toute la durée de l’instruction.In an unsafe context, the embedded_statement (Statements) production permits an additional construct, the fixed statement, which is used to "fix" a moveable variable such that its address remains constant for the duration of the statement.

fixed_statement
    : 'fixed' '(' pointer_type fixed_pointer_declarators ')' embedded_statement
    ;

fixed_pointer_declarators
    : fixed_pointer_declarator (','  fixed_pointer_declarator)*
    ;

fixed_pointer_declarator
    : identifier '=' fixed_pointer_initializer
    ;

fixed_pointer_initializer
    : '&' variable_reference
    | expression
    ;

Chaque fixed_pointer_declarator déclare une variable locale du pointer_type donné et initialise cette variable locale avec l’adresse calculée par le fixed_pointer_initializercorrespondant.Each fixed_pointer_declarator declares a local variable of the given pointer_type and initializes that local variable with the address computed by the corresponding fixed_pointer_initializer. Une variable locale déclarée dans une instruction fixed est accessible dans les fixed_pointer_initializers qui se produisent à droite de la déclaration de cette variable et dans le embedded_statement de l’instruction fixed.A local variable declared in a fixed statement is accessible in any fixed_pointer_initializers occurring to the right of that variable's declaration, and in the embedded_statement of the fixed statement. Une variable locale déclarée par une instruction fixed est considérée comme étant en lecture seule.A local variable declared by a fixed statement is considered read-only. Une erreur de compilation se produit si l’instruction incorporée tente de modifier cette variable locale (par le biais de l’assignation ou de ++ et -- opérateurs) ou de la passer en tant que paramètre ref ou out.A compile-time error occurs if the embedded statement attempts to modify this local variable (via assignment or the ++ and -- operators) or pass it as a ref or out parameter.

Un fixed_pointer_initializer peut être l’un des éléments suivants :A fixed_pointer_initializer can be one of the following:

  • Le jeton «&» suivi d’un variable_reference (règles précises pour déterminer l’assignation définie) à une variable déplaçable (variables fixes et déplaçables) d’un type non managé T, à condition que le type T* soit implicitement convertible en type pointeur donné dans l’instruction fixed.The token "&" followed by a variable_reference (Precise rules for determining definite assignment) to a moveable variable (Fixed and moveable variables) of an unmanaged type T, provided the type T* is implicitly convertible to the pointer type given in the fixed statement. Dans ce cas, l’initialiseur calcule l’adresse de la variable donnée et il est garanti que la variable reste à une adresse fixe pendant la durée de l’instruction fixed.In this case, the initializer computes the address of the given variable, and the variable is guaranteed to remain at a fixed address for the duration of the fixed statement.
  • Expression d’un array_type avec des éléments d’un type non managé T, à condition que le type T* soit implicitement convertible en type pointeur donné dans l’instruction fixed.An expression of an array_type with elements of an unmanaged type T, provided the type T* is implicitly convertible to the pointer type given in the fixed statement. Dans ce cas, l’initialiseur calcule l’adresse du premier élément du tableau, et le tableau entier est garanti qu’il reste à une adresse fixe pendant la durée de l’instruction fixed.In this case, the initializer computes the address of the first element in the array, and the entire array is guaranteed to remain at a fixed address for the duration of the fixed statement. Si l’expression de tableau a la valeur null ou si le tableau n’a aucun élément, l’initialiseur calcule une adresse égale à zéro.If the array expression is null or if the array has zero elements, the initializer computes an address equal to zero.
  • Expression de type string, à condition que le type char* soit implicitement convertible en type pointeur donné dans l’instruction fixed.An expression of type string, provided the type char* is implicitly convertible to the pointer type given in the fixed statement. Dans ce cas, l’initialiseur calcule l’adresse du premier caractère de la chaîne, et la chaîne entière est garantie pour rester à une adresse fixe pendant la durée de l’instruction fixed.In this case, the initializer computes the address of the first character in the string, and the entire string is guaranteed to remain at a fixed address for the duration of the fixed statement. Le comportement de l’instruction fixed est défini par l’implémentation si l’expression de chaîne est null.The behavior of the fixed statement is implementation-defined if the string expression is null.
  • Simple_name ou member_access qui référence un membre de mémoire tampon de taille fixe d’une variable déplaçable, à condition que le type du membre de la mémoire tampon de taille fixe soit implicitement convertible en type pointeur donné dans l’instruction fixed.A simple_name or member_access that references a fixed size buffer member of a moveable variable, provided the type of the fixed size buffer member is implicitly convertible to the pointer type given in the fixed statement. Dans ce cas, l’initialiseur calcule un pointeur vers le premier élément de la mémoire tampon de taille fixe (mémoires tampons de taille fixe dans les expressions), et la mémoire tampon de taille fixe est garantie pour rester à une adresse fixe pendant la durée de l’instruction fixed.In this case, the initializer computes a pointer to the first element of the fixed size buffer (Fixed size buffers in expressions), and the fixed size buffer is guaranteed to remain at a fixed address for the duration of the fixed statement.

Pour chaque adresse calculée par un fixed_pointer_initializer l’instruction fixed garantit que la variable référencée par l’adresse n’est pas sujette à un déplacement ou une suppression par le garbage collector pendant la durée de l’instruction fixed.For each address computed by a fixed_pointer_initializer the fixed statement ensures that the variable referenced by the address is not subject to relocation or disposal by the garbage collector for the duration of the fixed statement. Par exemple, si l’adresse calculée par un fixed_pointer_initializer fait référence à un champ d’un objet ou à un élément d’une instance de tableau, l’instruction fixed garantit que l’instance d’objet conteneur n’est pas déplacée ou supprimée pendant la durée de vie de l’instruction.For example, if the address computed by a fixed_pointer_initializer references a field of an object or an element of an array instance, the fixed statement guarantees that the containing object instance is not relocated or disposed of during the lifetime of the statement.

Il incombe au programmeur de s’assurer que les pointeurs créés par fixed instructions ne survivent pas au-delà de l’exécution de ces instructions.It is the programmer's responsibility to ensure that pointers created by fixed statements do not survive beyond execution of those statements. Par exemple, lorsque des pointeurs créés par des instructions fixed sont passés à des API externes, il incombe au programmeur de s’assurer que les API ne conservent aucune mémoire de ces pointeurs.For example, when pointers created by fixed statements are passed to external APIs, it is the programmer's responsibility to ensure that the APIs retain no memory of these pointers.

Les objets fixes peuvent entraîner la fragmentation du tas (car ils ne peuvent pas être déplacés).Fixed objects may cause fragmentation of the heap (because they can't be moved). Pour cette raison, les objets doivent être corrigés uniquement lorsque cela est absolument nécessaire, puis uniquement pour la durée la plus faible possible.For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible.

L’exempleThe example

class Test
{
    static int x;
    int y;

    unsafe static void F(int* p) {
        *p = 1;
    }

    static void Main() {
        Test t = new Test();
        int[] a = new int[10];
        unsafe {
            fixed (int* p = &x) F(p);
            fixed (int* p = &t.y) F(p);
            fixed (int* p = &a[0]) F(p);
            fixed (int* p = a) F(p);
        }
    }
}

illustre plusieurs utilisations de l’instruction fixed.demonstrates several uses of the fixed statement. La première instruction corrige et obtient l’adresse d’un champ statique, la deuxième instruction corrige et obtient l’adresse d’un champ d’instance, et la troisième instruction corrige et obtient l’adresse d’un élément de tableau.The first statement fixes and obtains the address of a static field, the second statement fixes and obtains the address of an instance field, and the third statement fixes and obtains the address of an array element. Dans chaque cas, il y aurait eu une erreur à utiliser l’opérateur de & régulière puisque les variables sont toutes classées comme variables déplaçables.In each case it would have been an error to use the regular & operator since the variables are all classified as moveable variables.

La quatrième instruction fixed dans l’exemple ci-dessus produit un résultat similaire à la troisième.The fourth fixed statement in the example above produces a similar result to the third.

Cet exemple de l’instruction fixed utilise string:This example of the fixed statement uses string:

class Test
{
    static string name = "xx";

    unsafe static void F(char* p) {
        for (int i = 0; p[i] != '\0'; ++i)
            Console.WriteLine(p[i]);
    }

    static void Main() {
        unsafe {
            fixed (char* p = name) F(p);
            fixed (char* p = "xx") F(p);
        }
    }
}

Dans un tableau de contexte non sécurisé, les éléments des tableaux unidimensionnels sont stockés dans l’ordre d’index de plus en plus important, en commençant par l’index 0 et en terminant par l’index Length - 1.In an unsafe context array elements of single-dimensional arrays are stored in increasing index order, starting with index 0 and ending with index Length - 1. Pour les tableaux multidimensionnels, les éléments de tableau sont stockés de telle sorte que les index de la dimension la plus à droite soient augmentés en premier, puis la dimension gauche suivante, et ainsi de suite vers la gauche.For multi-dimensional arrays, array elements are stored such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left. Dans une instruction fixed qui obtient un pointeur p à une instance de tableau a, les valeurs de pointeur allant de p à p + a.Length - 1 représentent les adresses des éléments dans le tableau.Within a fixed statement that obtains a pointer p to an array instance a, the pointer values ranging from p to p + a.Length - 1 represent addresses of the elements in the array. De même, les variables allant de p[0] à p[a.Length - 1] représentent les éléments de tableau réels.Likewise, the variables ranging from p[0] to p[a.Length - 1] represent the actual array elements. Étant donné la façon dont les tableaux sont stockés, nous pouvons traiter un tableau de toute dimension comme s’il était linéaire.Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear.

Exemple :For example:

using System;

class Test
{
    static void Main() {
        int[,,] a = new int[2,3,4];
        unsafe {
            fixed (int* p = a) {
                for (int i = 0; i < a.Length; ++i)    // treat as linear
                    p[i] = i;
            }
        }

        for (int i = 0; i < 2; ++i)
            for (int j = 0; j < 3; ++j) {
                for (int k = 0; k < 4; ++k)
                    Console.Write("[{0},{1},{2}] = {3,2} ", i, j, k, a[i,j,k]);
                Console.WriteLine();
            }
    }
}

ce qui génère la sortie :which produces the output:

[0,0,0] =  0 [0,0,1] =  1 [0,0,2] =  2 [0,0,3] =  3
[0,1,0] =  4 [0,1,1] =  5 [0,1,2] =  6 [0,1,3] =  7
[0,2,0] =  8 [0,2,1] =  9 [0,2,2] = 10 [0,2,3] = 11
[1,0,0] = 12 [1,0,1] = 13 [1,0,2] = 14 [1,0,3] = 15
[1,1,0] = 16 [1,1,1] = 17 [1,1,2] = 18 [1,1,3] = 19
[1,2,0] = 20 [1,2,1] = 21 [1,2,2] = 22 [1,2,3] = 23

Dans l’exempleIn the example

class Test
{
    unsafe static void Fill(int* p, int count, int value) {
        for (; count != 0; count--) *p++ = value;
    }

    static void Main() {
        int[] a = new int[100];
        unsafe {
            fixed (int* p = a) Fill(p, 100, -1);
        }
    }
}

une instruction fixed est utilisée pour corriger un tableau afin que son adresse puisse être passée à une méthode qui prend un pointeur.a fixed statement is used to fix an array so its address can be passed to a method that takes a pointer.

Dans l'exemple :In the example:

unsafe struct Font
{
    public int size;
    public fixed char name[32];
}

class Test
{
    unsafe static void PutString(string s, char* buffer, int bufSize) {
        int len = s.Length;
        if (len > bufSize) len = bufSize;
        for (int i = 0; i < len; i++) buffer[i] = s[i];
        for (int i = len; i < bufSize; i++) buffer[i] = (char)0;
    }

    Font f;

    unsafe static void Main()
    {
        Test test = new Test();
        test.f.size = 10;
        fixed (char* p = test.f.name) {
            PutString("Times New Roman", p, 32);
        }
    }
}

une instruction fixed est utilisée pour corriger une mémoire tampon de taille fixe d’un struct afin que son adresse puisse être utilisée comme pointeur.a fixed statement is used to fix a fixed size buffer of a struct so its address can be used as a pointer.

Une valeur char* produite en résolvant une instance de chaîne pointe toujours vers une chaîne terminée par le caractère null.A char* value produced by fixing a string instance always points to a null-terminated string. Dans une instruction fixed qui obtient un pointeur p à une instance de chaîne s, les valeurs de pointeur comprises entre p et p + s.Length - 1 représentent les adresses des caractères de la chaîne, tandis que la valeur de pointeur p + s.Length pointe toujours vers un caractère null (le caractère avec la valeur '\0').Within a fixed statement that obtains a pointer p to a string instance s, the pointer values ranging from p to p + s.Length - 1 represent addresses of the characters in the string, and the pointer value p + s.Length always points to a null character (the character with value '\0').

La modification d’objets de type managé par le biais de pointeurs fixes peut engendrer un comportement indéfini.Modifying objects of managed type through fixed pointers can results in undefined behavior. Par exemple, étant donné que les chaînes sont immuables, il incombe au programmeur de s’assurer que les caractères référencés par un pointeur vers une chaîne fixe ne sont pas modifiés.For example, because strings are immutable, it is the programmer's responsibility to ensure that the characters referenced by a pointer to a fixed string are not modified.

L’arrêt automatique des chaînes de caractères est particulièrement pratique lors de l’appel d’API externes qui attendent des chaînes de style C.The automatic null-termination of strings is particularly convenient when calling external APIs that expect "C-style" strings. Notez, toutefois, qu’une instance de chaîne est autorisée à contenir des caractères null.Note, however, that a string instance is permitted to contain null characters. Si ces caractères Null sont présents, la chaîne apparaît tronquée lorsqu’elle est traitée comme un char*se terminant par un caractère null.If such null characters are present, the string will appear truncated when treated as a null-terminated char*.

Mémoires tampons de taille fixeFixed size buffers

Les mémoires tampons de taille fixe sont utilisées pour déclarer des tableaux en ligne « C style » comme membres de structs, et sont principalement utiles pour l’interfaçage avec les API non managées.Fixed size buffers are used to declare "C style" in-line arrays as members of structs, and are primarily useful for interfacing with unmanaged APIs.

Déclarations de mémoire tampon de taille fixeFixed size buffer declarations

Une mémoire tampon de taille fixe est un membre qui représente le stockage pour une mémoire tampon de longueur fixe des variables d’un type donné.A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. Une déclaration de mémoire tampon de taille fixe introduit une ou plusieurs mémoires tampons de taille fixe d’un type d’élément donné.A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. Les mémoires tampons de taille fixe sont autorisées uniquement dans les déclarations de struct et peuvent se produire uniquement dans des contextes non sécurisés (contextes non sécurisés).Fixed size buffers are only permitted in struct declarations and can only occur in unsafe contexts (Unsafe contexts).

struct_member_declaration_unsafe
    : fixed_size_buffer_declaration
    ;

fixed_size_buffer_declaration
    : attributes? fixed_size_buffer_modifier* 'fixed' buffer_element_type fixed_size_buffer_declarator+ ';'
    ;

fixed_size_buffer_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'unsafe'
    ;

buffer_element_type
    : type
    ;

fixed_size_buffer_declarator
    : identifier '[' constant_expression ']'
    ;

Une déclaration de mémoire tampon de taille fixe peut inclure un ensemble d’attributs (attributs), un modificateur de new (modificateurs), une combinaison valide des quatre modificateurs d’accès (paramètres de type et contraintes) et un modificateur de unsafe (contextes non sécurisés).A fixed size buffer declaration may include a set of attributes (Attributes), a new modifier (Modifiers), a valid combination of the four access modifiers (Type parameters and constraints) and an unsafe modifier (Unsafe contexts). Les attributs et les modificateurs s’appliquent à tous les membres déclarés par la déclaration de mémoire tampon de taille fixe.The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. Il s’agit d’une erreur pour que le même modificateur apparaisse plusieurs fois dans une déclaration de mémoire tampon de taille fixe.It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.

Une déclaration de mémoire tampon de taille fixe n’est pas autorisée à inclure le modificateur static.A fixed size buffer declaration is not permitted to include the static modifier.

Le type d’élément de mémoire tampon d’une déclaration de mémoire tampon de taille fixe spécifie le type d’élément de la ou des mémoires tampons introduites par la déclaration.The buffer element type of a fixed size buffer declaration specifies the element type of the buffer(s) introduced by the declaration. Le type d’élément de mémoire tampon doit être l’un des types prédéfinis sbyte, byte, short, ushort, int, uint, long, ulong, char, float, doubleou bool.The buffer element type must be one of the predefined types sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or bool.

Le type d’élément de mémoire tampon est suivi d’une liste de déclarateurs de mémoires tampons de taille fixe, chacun d’entre eux introduisant un nouveau membre.The buffer element type is followed by a list of fixed size buffer declarators, each of which introduces a new member. Un déclarateur de mémoire tampon de taille fixe se compose d’un identificateur qui nomme le membre, suivi d’une expression constante placée entre [ et ] jetons.A fixed size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in [ and ] tokens. L’expression constante indique le nombre d’éléments dans le membre introduit par ce déclarateur de mémoire tampon de taille fixe.The constant expression denotes the number of elements in the member introduced by that fixed size buffer declarator. Le type de l’expression constante doit être implicitement convertible en type int, et la valeur doit être un entier positif différent de zéro.The type of the constant expression must be implicitly convertible to type int, and the value must be a non-zero positive integer.

Il est garanti que les éléments d’une mémoire tampon de taille fixe sont disposés de façon séquentielle dans la mémoire.The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.

Une déclaration de mémoire tampon de taille fixe qui déclare plusieurs mémoires tampons de taille fixe équivaut à plusieurs déclarations d’une déclaration de mémoire tampon de taille fixe unique avec les mêmes attributs et types d’éléments.A fixed size buffer declaration that declares multiple fixed size buffers is equivalent to multiple declarations of a single fixed size buffer declaration with the same attributes, and element types. Exemple :For example

unsafe struct A
{
   public fixed int x[5], y[10], z[100];
}

est équivalent àis equivalent to

unsafe struct A
{
   public fixed int x[5];
   public fixed int y[10];
   public fixed int z[100];
}

Mémoires tampons de taille fixe dans les expressionsFixed size buffers in expressions

La recherche de membre (opérateurs) d’un membre de mémoire tampon de taille fixe se déroule exactement comme la recherche de membres d’un champ.Member lookup (Operators) of a fixed size buffer member proceeds exactly like member lookup of a field.

Une mémoire tampon de taille fixe peut être référencée dans une expression à l’aide d’un simple_name (inférence de type) ou d’une member_access (vérification au moment de la compilation de la résolution de surcharge dynamique).A fixed size buffer can be referenced in an expression using a simple_name (Type inference) or a member_access (Compile-time checking of dynamic overload resolution).

Lorsqu’un membre de la mémoire tampon de taille fixe est référencé comme nom simple, l’effet est le même qu’un accès aux membres de la forme this.I, où I est le membre de mémoire tampon de taille fixe.When a fixed size buffer member is referenced as a simple name, the effect is the same as a member access of the form this.I, where I is the fixed size buffer member.

Dans un accès aux membres de la forme E.I, si E est d’un type struct et qu’une recherche de membre de I dans ce type struct identifie un membre de taille fixe, E.I est évaluée comme suit :In a member access of the form E.I, if E is of a struct type and a member lookup of I in that struct type identifies a fixed size member, then E.I is evaluated an classified as follows:

  • Si l’expression E.I ne se produit pas dans un contexte non sécurisé, une erreur de compilation se produit.If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
  • Si E est classée en tant que valeur, une erreur de compilation se produit.If E is classified as a value, a compile-time error occurs.
  • Sinon, si E est une variable déplaçable (variables fixes et déplaçables) et que l’expression E.I n’est pas un fixed_pointer_initializer (l’instruction fixed), une erreur de compilation se produit.Otherwise, if E is a moveable variable (Fixed and moveable variables) and the expression E.I is not a fixed_pointer_initializer (The fixed statement), a compile-time error occurs.
  • Dans le cas contraire, E fait référence à une variable fixe et le résultat de l’expression est un pointeur vers le premier élément du membre de la mémoire tampon de taille fixe I dans E.Otherwise, E references a fixed variable and the result of the expression is a pointer to the first element of the fixed size buffer member I in E. Le résultat est de type S*, où S est le type d’élément de Iet est classifié comme une valeur.The result is of type S*, where S is the element type of I, and is classified as a value.

Les éléments suivants de la mémoire tampon de taille fixe sont accessibles à l’aide d’opérations de pointeur à partir du premier élément.The subsequent elements of the fixed size buffer can be accessed using pointer operations from the first element. Contrairement à l’accès aux tableaux, l’accès aux éléments d’une mémoire tampon de taille fixe est une opération risquée et l’option n’est pas cochée.Unlike access to arrays, access to the elements of a fixed size buffer is an unsafe operation and is not range checked.

L’exemple suivant déclare et utilise un struct avec un membre de mémoire tampon de taille fixe.The following example declares and uses a struct with a fixed size buffer member.

unsafe struct Font
{
    public int size;
    public fixed char name[32];
}

class Test
{
    unsafe static void PutString(string s, char* buffer, int bufSize) {
        int len = s.Length;
        if (len > bufSize) len = bufSize;
        for (int i = 0; i < len; i++) buffer[i] = s[i];
        for (int i = len; i < bufSize; i++) buffer[i] = (char)0;
    }

    unsafe static void Main()
    {
        Font f;
        f.size = 10;
        PutString("Times New Roman", f.name, 32);
    }
}

Vérification de l’attribution définieDefinite assignment checking

Les mémoires tampons de taille fixe ne sont pas soumises à la vérification de l’assignation définie (assignation définie) et les membres de la mémoire tampon de taille fixe sont ignorés à des fins de vérification de l’assignation définie des variables de type struct.Fixed size buffers are not subject to definite assignment checking (Definite assignment), and fixed size buffer members are ignored for purposes of definite assignment checking of struct type variables.

Lorsque la variable de struct conteneur la plus externe d’un membre de mémoire tampon de taille fixe est une variable statique, une variable d’instance d’une instance de classe ou un élément de tableau, les éléments de la mémoire tampon de taille fixe sont automatiquement initialisés à leurs valeurs par défaut (valeurs par défaut).When the outermost containing struct variable of a fixed size buffer member is a static variable, an instance variable of a class instance, or an array element, the elements of the fixed size buffer are automatically initialized to their default values (Default values). Dans tous les autres cas, le contenu initial d’une mémoire tampon de taille fixe n’est pas défini.In all other cases, the initial content of a fixed size buffer is undefined.

Allocation de pileStack allocation

Dans un contexte non sécurisé, une déclaration de variable locale (déclarations de variable locale) peut inclure un initialiseur d’allocation de pile qui alloue de la mémoire à partir de la pile des appels.In an unsafe context, a local variable declaration (Local variable declarations) may include a stack allocation initializer which allocates memory from the call stack.

local_variable_initializer_unsafe
    : stackalloc_initializer
    ;

stackalloc_initializer
    : 'stackalloc' unmanaged_type '[' expression ']'
    ;

Le unmanaged_type indique le type des éléments qui seront stockés dans l’emplacement nouvellement alloué, et l' expression indique le nombre de ces éléments.The unmanaged_type indicates the type of the items that will be stored in the newly allocated location, and the expression indicates the number of these items. Prises ensemble, elles spécifient la taille d’allocation requise.Taken together, these specify the required allocation size. Étant donné que la taille d’une allocation de pile ne peut pas être négative, il s’agit d’une erreur de compilation pour spécifier le nombre d’éléments sous la forme d’un constant_expression qui correspond à une valeur négative.Since the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a constant_expression that evaluates to a negative value.

Un initialiseur d’allocation de pile de la forme stackalloc T[E] nécessite que T soit un type non managé (types pointeur) et E en tant qu’expression de type int.A stack allocation initializer of the form stackalloc T[E] requires T to be an unmanaged type (Pointer types) and E to be an expression of type int. La construction alloue E * sizeof(T) octets de la pile des appels et retourne un pointeur, de type T*, au bloc nouvellement alloué.The construct allocates E * sizeof(T) bytes from the call stack and returns a pointer, of type T*, to the newly allocated block. Si E est une valeur négative, le comportement n’est pas défini.If E is a negative value, then the behavior is undefined. Si E est égal à zéro, aucune allocation n’est effectuée et le pointeur retourné est défini par l’implémentation.If E is zero, then no allocation is made, and the pointer returned is implementation-defined. Si la mémoire disponible est insuffisante pour allouer un bloc de la taille donnée, une System.StackOverflowException est levée.If there is not enough memory available to allocate a block of the given size, a System.StackOverflowException is thrown.

Le contenu de la mémoire nouvellement allouée n’est pas défini.The content of the newly allocated memory is undefined.

Les initialiseurs d’allocation de pile ne sont pas autorisés dans les blocs catch ou finally (instruction try).Stack allocation initializers are not permitted in catch or finally blocks (The try statement).

Il n’existe aucun moyen de libérer explicitement la mémoire allouée à l’aide de stackalloc.There is no way to explicitly free memory allocated using stackalloc. Tous les blocs de mémoire alloués par la pile créés lors de l’exécution d’un membre de fonction sont automatiquement ignorés quand ce membre de fonction retourne.All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. Cela correspond à la fonction alloca, une extension couramment trouvée dans C et C++ les implémentations.This corresponds to the alloca function, an extension commonly found in C and C++ implementations.

Dans l’exempleIn the example

using System;

class Test
{
    static string IntToString(int value) {
        int n = value >= 0? value: -value;
        unsafe {
            char* buffer = stackalloc char[16];
            char* p = buffer + 16;
            do {
                *--p = (char)(n % 10 + '0');
                n /= 10;
            } while (n != 0);
            if (value < 0) *--p = '-';
            return new string(p, 0, (int)(buffer + 16 - p));
        }
    }

    static void Main() {
        Console.WriteLine(IntToString(12345));
        Console.WriteLine(IntToString(-999));
    }
}

un initialiseur de stackalloc est utilisé dans la méthode IntToString pour allouer une mémoire tampon de 16 caractères sur la pile.a stackalloc initializer is used in the IntToString method to allocate a buffer of 16 characters on the stack. La mémoire tampon est automatiquement supprimée lorsque la méthode est retournée.The buffer is automatically discarded when the method returns.

Allocation de mémoire dynamiqueDynamic memory allocation

À l’exception de l’opérateur C# stackalloc, ne fournit aucune construction prédéfinie pour la gestion de la mémoire qui n’est pas récupérée par le garbage collector.Except for the stackalloc operator, C# provides no predefined constructs for managing non-garbage collected memory. Ces services sont généralement fournis par la prise en charge des bibliothèques de classes ou importés directement depuis le système d’exploitation sous-jacent.Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. Par exemple, la classe Memory ci-dessous illustre la façon dont les fonctions de tas d’un système d’exploitation C#sous-jacent sont accessibles à partir de :For example, the Memory class below illustrates how the heap functions of an underlying operating system might be accessed from C#:

using System;
using System.Runtime.InteropServices;

public static unsafe class Memory
{
    // Handle for the process heap. This handle is used in all calls to the
    // HeapXXX APIs in the methods below.
    private static readonly IntPtr s_heap = GetProcessHeap();

    // Allocates a memory block of the given size. The allocated memory is
    // automatically initialized to zero.
    public static void* Alloc(int size)
    {
        void* result = HeapAlloc(s_heap, HEAP_ZERO_MEMORY, (UIntPtr)size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }

    // Copies count bytes from src to dst. The source and destination
    // blocks are permitted to overlap.
    public static void Copy(void* src, void* dst, int count)
    {
        byte* ps = (byte*)src;
        byte* pd = (byte*)dst;
        if (ps > pd)
        {
            for (; count != 0; count--) *pd++ = *ps++;
        }
        else if (ps < pd)
        {
            for (ps += count, pd += count; count != 0; count--) *--pd = *--ps;
        }
    }

    // Frees a memory block.
    public static void Free(void* block)
    {
        if (!HeapFree(s_heap, 0, block)) throw new InvalidOperationException();
    }

    // Re-allocates a memory block. If the reallocation request is for a
    // larger size, the additional region of memory is automatically
    // initialized to zero.
    public static void* ReAlloc(void* block, int size)
    {
        void* result = HeapReAlloc(s_heap, HEAP_ZERO_MEMORY, block, (UIntPtr)size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }

    // Returns the size of a memory block.
    public static int SizeOf(void* block)
    {
        int result = (int)HeapSize(s_heap, 0, block);
        if (result == -1) throw new InvalidOperationException();
        return result;
    }

    // Heap API flags
    private const int HEAP_ZERO_MEMORY = 0x00000008;

    // Heap API functions
    [DllImport("kernel32")]
    private static extern IntPtr GetProcessHeap();

    [DllImport("kernel32")]
    private static extern void* HeapAlloc(IntPtr hHeap, int flags, UIntPtr size);

    [DllImport("kernel32")]
    private static extern bool HeapFree(IntPtr hHeap, int flags, void* block);

    [DllImport("kernel32")]
    private static extern void* HeapReAlloc(IntPtr hHeap, int flags, void* block, UIntPtr size);

    [DllImport("kernel32")]
    private static extern UIntPtr HeapSize(IntPtr hHeap, int flags, void* block);
}

Vous trouverez ci-dessous un exemple qui utilise la classe Memory :An example that uses the Memory class is given below:

class Test
{
    static unsafe void Main()
    {
        byte* buffer = null;
        try
        {
            const int Size = 256;
            buffer = (byte*)Memory.Alloc(Size);
            for (int i = 0; i < Size; i++) buffer[i] = (byte)i;
            byte[] array = new byte[Size];
            fixed (byte* p = array) Memory.Copy(buffer, p, Size);
            for (int i = 0; i < Size; i++) Console.WriteLine(array[i]);
        }
        finally
        {
            if (buffer != null) Memory.Free(buffer);
        }
    }
}

L’exemple alloue 256 octets de mémoire via Memory.Alloc et initialise le bloc de mémoire avec des valeurs qui s’incrémentent de 0 à 255.The example allocates 256 bytes of memory through Memory.Alloc and initializes the memory block with values increasing from 0 to 255. Il alloue ensuite un tableau d’octets d’éléments 256 et utilise Memory.Copy pour copier le contenu du bloc de mémoire dans le tableau d’octets.It then allocates a 256 element byte array and uses Memory.Copy to copy the contents of the memory block into the byte array. Enfin, le bloc de mémoire est libéré à l’aide de Memory.Free et le contenu du tableau d’octets est généré sur la console.Finally, the memory block is freed using Memory.Free and the contents of the byte array are output on the console.