Codice di tipo unsafeUnsafe code

Il linguaggio C# di base, come definito nei capitoli precedenti, differisce in particolare da C e C++ nell'omissione dei puntatori come tipo di dati.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. C# Fornisce invece riferimenti e la possibilità di creare oggetti gestiti da un Garbage Collector.Instead, C# provides references and the ability to create objects that are managed by a garbage collector. Questo progetto, associato ad altre funzionalità, rende C# un linguaggio molto più sicuro rispetto a C C++o.This design, coupled with other features, makes C# a much safer language than C or C++. Nel linguaggio di C# base non è semplicemente possibile avere una variabile non inizializzata, un puntatore "penzoloni" o un'espressione che indicizza una matrice oltre i limiti.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. Vengono eliminate tutte le categorie di bug che periodicamente affliggono C++ C e i programmi.Whole categories of bugs that routinely plague C and C++ programs are thus eliminated.

Sebbene praticamente ogni costrutto di tipo di puntatore C++ in C o abbia una controparte del tipo di riferimento in, tuttavia, esistono situazioni in C#cui l'accesso ai tipi di puntatore diventa una necessità.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. Ad esempio, l'interazione con il sistema operativo sottostante, l'accesso a un dispositivo mappato alla memoria o l'implementazione di un algoritmo critico per il tempo potrebbe non essere possibile o praticabile senza l'accesso ai puntatori.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. Per rispondere a questa esigenza C# , offre la possibilità di scrivere codice unsafe.To address this need, C# provides the ability to write unsafe code.

In codice unsafe è possibile dichiarare e operare sui puntatori, per eseguire conversioni tra puntatori e tipi integrali, per prendere l'indirizzo delle variabili e così via.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. In un certo senso, la scrittura di codice non sicuro è molto simile alla scrittura C# di codice C all'interno di un programma.In a sense, writing unsafe code is much like writing C code within a C# program.

Il codice di tipo unsafe è infatti una funzionalità "sicura" dal punto di vista di sviluppatori e utenti.Unsafe code is in fact a "safe" feature from the perspective of both developers and users. Il codice di tipo unsafe deve essere contrassegnato chiaramente con il modificatore unsafe, in modo che gli sviluppatori non possano usare accidentalmente funzionalità non sicure e il motore di esecuzione funzioni per garantire che il codice non sicuro non possa essere eseguito in un ambiente non attendibile.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.

Contesti non sicuriUnsafe contexts

Le funzionalità unsafe di C# sono disponibili solo in contesti non sicuri.The unsafe features of C# are available only in unsafe contexts. Un contesto unsafe viene introdotto includendo un modificatore unsafe nella dichiarazione di un tipo o di un membro oppure utilizzando 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:

  • Una dichiarazione di una classe, una struttura, un'interfaccia o un delegato può includere un modificatore unsafe, nel qual caso l'intero extent testuale della dichiarazione del tipo (incluso il corpo della classe, dello struct o dell'interfaccia) viene considerato un contesto non sicuro.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.
  • Una dichiarazione di un campo, un metodo, una proprietà, un evento, un indicizzatore, un operatore, un costruttore di istanza, un distruttore o un costruttore statico può includere un modificatore unsafe, nel qual caso l'intero extent testuale della dichiarazione del membro viene considerato un contesto non sicuro.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.
  • Un unsafe_statement consente l'uso di un contesto non sicuro all'interno di un blocco.An unsafe_statement enables the use of an unsafe context within a block. L'intero extent testuale del blocco associato viene considerato un contesto non sicuro.The entire textual extent of the associated block is considered an unsafe context.

Le produzioni di grammatica associate sono illustrate di seguito.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
    ;

Nell'esempioIn the example

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

il modificatore unsafe specificato nella dichiarazione struct fa in modo che l'intero extent testuale della dichiarazione struct diventi un contesto non sicuro.the unsafe modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. È quindi possibile dichiarare i campi Left e Right in modo che siano di tipo puntatore.Thus, it is possible to declare the Left and Right fields to be of a pointer type. È possibile scrivere anche l'esempio precedenteThe example above could also be written

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

In questo caso, i modificatori unsafe nelle dichiarazioni di campo comportano la considerazione di tali dichiarazioni come contesti non sicuri.Here, the unsafe modifiers in the field declarations cause those declarations to be considered unsafe contexts.

Oltre a stabilire un contesto non sicuro, consentendo così l'uso di tipi di puntatore, il modificatore unsafe non ha alcun effetto su un tipo o un membro.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. Nell'esempioIn the example

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

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

il modificatore unsafe nel metodo F in A fa semplicemente sì che l'estensione testuale di F diventi un contesto non sicuro in cui è possibile usare le funzionalità non sicure del linguaggio.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. Nell'override di F in B, non è necessario specificare nuovamente il modificatore unsafe, a meno che, naturalmente, il metodo F in B debba accedere a funzionalità non sicure.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 situazione è leggermente diversa quando un tipo di puntatore fa parte della firma del metodoThe 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) {...}
}

Qui, dal momento che la firma di F include un tipo di puntatore, può essere scritta solo in un contesto unsafe.Here, because F's signature includes a pointer type, it can only be written in an unsafe context. Tuttavia, il contesto unsafe può essere introdotto rendendo l'intera classe unsafe, come nel caso di A o includendo un modificatore unsafe nella dichiarazione del metodo, come nel caso di 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.

Tipi puntatorePointer types

In un contesto unsafe, un tipo (tipi) può essere pointer_type , oltre a un value_type o reference_type.In an unsafe context, a type (Types) may be a pointer_type as well as a value_type or a reference_type. Tuttavia, un pointer_type può essere usato anche in un'espressione typeof (espressioni di creazione di oggetti anonimi) all'esterno di un contesto non sicuro perché tale utilizzo non è sicuro.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 viene scritto come unmanaged_type o parola chiave void, seguito da un token *: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
    ;

Il tipo specificato prima del * in un tipo di puntatore è denominato tipo referente del tipo di puntatore.The type specified before the * in a pointer type is called the referent type of the pointer type. Rappresenta il tipo della variabile a cui fa riferimento un valore del tipo di puntatore.It represents the type of the variable to which a value of the pointer type points.

A differenza dei riferimenti (valori dei tipi di riferimento), i puntatori non vengono rilevati dal Garbage Collector, il Garbage Collector non conosce i puntatori e i dati a cui puntano.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. Per questo motivo un puntatore non può puntare a un riferimento o a uno struct che contiene riferimenti e il tipo referente di un puntatore deve essere 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 è un tipo che non è un reference_type o un tipo costruito e non contiene reference_type o campi di tipo costruiti a qualsiasi livello di annidamento.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. In altre parole, un unmanaged_type è uno dei seguenti:In other words, an unmanaged_type is one of the following:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, 0, 1 o 2.sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Qualsiasi enum_type.Any enum_type.
  • Qualsiasi pointer_type.Any pointer_type.
  • Qualsiasi struct_type definito dall'utente che non è un tipo costruito e contiene solo i campi di unmanaged_type.Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.

La regola intuitiva per la combinazione di puntatori e riferimenti è che i riferimenti di riferimenti (oggetti) sono consentiti per contenere i puntatori, ma i riferimenti ai puntatori non possono contenere riferimenti.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.

Nella tabella seguente sono riportati alcuni esempi di tipi di puntatore:Some examples of pointer types are given in the table below:

EsempioExample DescrizioneDescription
byte* Puntatore a bytePointer to byte
char* Puntatore a charPointer to char
int** Puntatore a puntatore a intPointer to pointer to int
int*[] Matrice unidimensionale di puntatori a intSingle-dimensional array of pointers to int
void* Puntatore al tipo sconosciutoPointer to unknown type

Per un'implementazione specifica, tutti i tipi di puntatore devono avere le stesse dimensioni e la stessa rappresentazione.For a given implementation, all pointer types must have the same size and representation.

Diversamente da C e C++, quando più puntatori vengono dichiarati nella stessa dichiarazione, C# nel * viene scritto insieme al tipo sottostante, non come un punteggiatore di prefisso per ogni nome di puntatore.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. Esempio:For example

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

Il valore di un puntatore con tipo T* rappresenta l'indirizzo di una variabile di tipo T.The value of a pointer having type T* represents the address of a variable of type T. Per accedere a questa variabile, è possibile usare l'operatore di riferimento indiretto del puntatore * (riferimento indiretto al puntatore).The pointer indirection operator * (Pointer indirection) may be used to access this variable. Ad esempio, data una variabile P di tipo int*, l'espressione *P indica la variabile int trovata all'indirizzo contenuto in P.For example, given a variable P of type int*, the expression *P denotes the int variable found at the address contained in P.

Analogamente a un riferimento a un oggetto, un puntatore può essere null.Like an object reference, a pointer may be null. L'applicazione dell'operatore di riferimento indiretto a un puntatore null comporta un comportamento definito dall'implementazione.Applying the indirection operator to a null pointer results in implementation-defined behavior. Un puntatore con valore null è rappresentato da All-bits-zero.A pointer with value null is represented by all-bits-zero.

Il tipo void* rappresenta un puntatore a un tipo sconosciuto.The void* type represents a pointer to an unknown type. Poiché il tipo referente è sconosciuto, non è possibile applicare l'operatore di riferimento indiretto a un puntatore di tipo void* né eseguire operazioni aritmetiche su tale puntatore.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. Tuttavia, è possibile eseguire il cast di un puntatore di tipo void* a qualsiasi altro tipo di puntatore (e viceversa).However, a pointer of type void* can be cast to any other pointer type (and vice versa).

I tipi di puntatore sono una categoria separata di tipi.Pointer types are a separate category of types. A differenza dei tipi di riferimento e dei tipi di valore, i tipi di puntatore non ereditano da object e non esistono conversioni tra tipi di puntatore e object.Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object. In particolare, la conversione boxing e unboxing (Boxing eunboxing) non sono supportate per i puntatori.In particular, boxing and unboxing (Boxing and unboxing) are not supported for pointers. Tuttavia, le conversioni sono consentite tra tipi di puntatore diversi e tra tipi di puntatore e tipi integrali.However, conversions are permitted between different pointer types and between pointer types and the integral types. Questa operazione è descritta in conversioni di puntatori.This is described in Pointer conversions.

Non è possibile usare pointer_type come argomento di tipo (tipi costruiti) e l'inferenza del tipo (inferenzadel tipo) ha esito negativo su chiamate a metodi generici che avrebbero dedotto un argomento di tipo come tipo di puntatore.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 può essere usato come tipo di un campo volatile (campi volatili).A pointer_type may be used as the type of a volatile field (Volatile fields).

Sebbene i puntatori possano essere passati come parametri ref o out, questa operazione può causare un comportamento non definito, poiché il puntatore può essere impostato in modo da puntare a una variabile locale che non esiste più quando il metodo chiamato restituisce o l'oggetto fisso a cui è stato usato per puntare , non è più fisso.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. Esempio: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
        }
    }
}

Un metodo può restituire un valore di un tipo e tale tipo può essere un puntatore.A method can return a value of some type, and that type can be a pointer. Se, ad esempio, si specifica un puntatore a una sequenza contigua di int, il numero di elementi di tale sequenza e un altro valore int, il metodo seguente restituisce l'indirizzo di tale valore nella sequenza, se si verifica una corrispondenza; in caso contrario, restituisce 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;
}

In un contesto non sicuro, sono disponibili diversi costrutti per operare sui puntatori:In an unsafe context, several constructs are available for operating on pointers:

Variabili fisse e mobiliFixed and moveable variables

L'operatore address-of (operatore address-of) e l'istruzione fixed (istruzione fixed) dividono le variabili in due categorie: Variabili fisse e variabili mobili.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.

Le variabili fisse si trovano in percorsi di archiviazione che non sono interessati dall'operazione del Garbage Collector.Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. Esempi di variabili fisse includono variabili locali, parametri di valore e variabili create mediante la dereferenziazione dei puntatori. D'altra parte, le variabili mobili si trovano in posizioni di archiviazione soggette alla rilocazione o all'eliminazione da parte del 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. (Esempi di variabili mobili includono campi in oggetti ed elementi di matrici).(Examples of moveable variables include fields in objects and elements of arrays.)

L'operatore & (l'operatore address-of) consente di ottenere l'indirizzo di una variabile fissa senza restrizioni.The & operator (The address-of operator) permits the address of a fixed variable to be obtained without restrictions. Tuttavia, poiché una variabile mobile è soggetta alla rilocazione o all'eliminazione da parte del Garbage Collector, l'indirizzo di una variabile mobile può essere ottenuto solo utilizzando un'istruzione fixed (l'istruzione fixed) e tale indirizzo rimane valido solo per il durata dell'istruzione 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.

In termini precisi, una variabile fissa è una delle seguenti:In precise terms, a fixed variable is one of the following:

  • Variabile risultante da un simple_name (nomi semplici) che fa riferimento a una variabile locale o a un parametro di valore, a meno che la variabile non venga acquisita da una funzione anonima.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.
  • Una variabile risultante da un member_access (accesso ai membri) nel formato V.I, dove V è una variabile fissa di 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.
  • Una variabile risultante da un pointer_indirection_expression (riferimento indiretto del puntatore) nel formato *P, un pointer_member_access (accesso al membro del puntatore) nel formato P->I o un pointer_element_access ( Accesso agli elementi del puntatore) nel formato P[E].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].

Tutte le altre variabili sono classificate come variabili mobili.All other variables are classified as moveable variables.

Si noti che un campo statico è classificato come variabile mobile.Note that a static field is classified as a moveable variable. Si noti inoltre che un parametro ref o out è classificato come variabile mobile, anche se l'argomento specificato per il parametro è una variabile fissa.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. Si noti infine che una variabile prodotta mediante la dereferenziazione di un puntatore viene sempre classificata come variabile fissa.Finally, note that a variable produced by dereferencing a pointer is always classified as a fixed variable.

Conversioni puntatorePointer conversions

In un contesto non sicuro, il set di conversioni implicite disponibili (conversioni implicite) viene esteso in modo da includere le seguenti conversioni implicite del puntatore:In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • Da qualsiasi pointer_type al tipo void*.From any pointer_type to the type void*.
  • Dal valore letterale null a qualsiasi pointer_type.From the null literal to any pointer_type.

Inoltre, in un contesto unsafe, il set di conversioni esplicite disponibili (conversioni esplicite) viene esteso in modo da includere le seguenti conversioni esplicite del puntatore:Additionally, in an unsafe context, the set of available explicit conversions (Explicit conversions) is extended to include the following explicit pointer conversions:

  • Da qualsiasi pointer_type a qualsiasi altro pointer_type.From any pointer_type to any other pointer_type.
  • Da sbyte, byte, short, ushort, int, uint, long o ulong a qualsiasi pointer_type.From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer_type.
  • Da qualsiasi pointer_type a sbyte, byte, short, ushort, int, uint, long o ulong.From any pointer_type to sbyte, byte, short, ushort, int, uint, long, or ulong.

Infine, in un contesto unsafe, il set di conversioni implicite standard (conversioni implicite standard) include la seguente conversione del puntatore:Finally, in an unsafe context, the set of standard implicit conversions (Standard implicit conversions) includes the following pointer conversion:

  • Da qualsiasi pointer_type al tipo void*.From any pointer_type to the type void*.

Le conversioni tra due tipi di puntatore non modificano mai il valore effettivo del puntatore.Conversions between two pointer types never change the actual pointer value. In altre parole, una conversione da un tipo di puntatore a un altro non ha alcun effetto sull'indirizzo sottostante fornito dal puntatore.In other words, a conversion from one pointer type to another has no effect on the underlying address given by the pointer.

Quando un tipo di puntatore viene convertito in un altro, se il puntatore risultante non è allineato correttamente per il tipo puntato, il comportamento non è definito se il risultato è dereferenziato.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. In generale, il concetto "allineato correttamente" è transitivo: se un puntatore al tipo A è allineato correttamente per un puntatore al tipo B, che, a sua volta, è allineato correttamente per un puntatore al tipo C, un puntatore al tipo A è correttamente allineato per un puntatore al tipo 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.

Si consideri il caso seguente in cui si accede a una variabile con un tipo tramite un puntatore a un tipo diverso: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

Quando un tipo di puntatore viene convertito in un puntatore a byte, il risultato fa riferimento al byte più basso risolto della variabile.When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the variable. Gli incrementi successivi del risultato, fino alla dimensione della variabile, restituiscono puntatori ai byte rimanenti della variabile.Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. Ad esempio, il metodo seguente visualizza ciascuno degli otto byte in un valore Double come valore esadecimale: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();
        }
    }
}

Naturalmente, l'output prodotto dipende da un tipo di output.Of course, the output produced depends on endianness.

I mapping tra i puntatori e i numeri interi vengono definiti dall'implementazione.Mappings between pointers and integers are implementation-defined. Tuttavia, nelle architetture della CPU 32 * e a 64 bit con uno spazio degli indirizzi lineare, le conversioni di puntatori a o da tipi integrali si comportano in genere esattamente come le conversioni dei valori uint o ulong, rispettivamente, a o da tali tipi integrali.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.

Matrici puntatorePointer arrays

In un contesto non sicuro è possibile costruire matrici di puntatori.In an unsafe context, arrays of pointers can be constructed. Nelle matrici di puntatore sono consentite solo alcune conversioni applicabili ad altri tipi di matrice:Only some of the conversions that apply to other array types are allowed on pointer arrays:

  • La conversione implicita dei riferimenti (conversioni implicite dei riferimenti) da qualsiasi array_type a System.Array e le interfacce implementate si applicano anche alle matrici del puntatore.The implicit reference conversion (Implicit reference conversions) from any array_type to System.Array and the interfaces it implements also applies to pointer arrays. Tuttavia, qualsiasi tentativo di accedere agli elementi della matrice tramite System.Array o le interfacce implementate genererà un'eccezione in fase di esecuzione, in quanto i tipi di puntatore non sono convertibili in 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.
  • Le conversioni di riferimenti implicite ed esplicite (conversioni implicitedei riferimenti, conversioni di riferimenti esplicite) da un tipo di matrice unidimensionale S[] a System.Collections.Generic.IList<T> e le interfacce di base generiche non si applicano mai alle matrici di puntatore, Poiché i tipi di puntatore non possono essere usati come argomenti di tipo e non ci sono conversioni dai tipi puntatore ai tipi non puntatore.The 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 conversione esplicita dei riferimenti (conversioni esplicite dei riferimenti) da System.Array e le interfacce implementate a qualsiasi array_type si applicano alle matrici del puntatore.The explicit reference conversion (Explicit reference conversions) from System.Array and the interfaces it implements to any array_type applies to pointer arrays.
  • Le conversioni esplicite dei riferimenti (conversioni esplicite dei riferimenti) da System.Collections.Generic.IList<S> e le relative interfacce di base a un tipo di matrice unidimensionale T[] non si applicano mai alle matrici di puntatore, perché i tipi di puntatore non possono essere utilizzati come argomenti di tipo e sono presenti Nessuna conversione dai tipi di puntatore ai tipi non puntatore.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.

Queste restrizioni indicano che l'espansione per l'istruzione foreach su matrici descritte nell' istruzione foreach non può essere applicata alle matrici di puntatore.These restrictions mean that the expansion for the foreach statement over arrays described in The foreach statement cannot be applied to pointer arrays. Al contrario, un'istruzione foreach nel formatoInstead, a foreach statement of the form

foreach (V v in x) embedded_statement

dove il tipo di x è un tipo di matrice nel formato T[,,...,], N è il numero di dimensioni meno 1 e T o V è un tipo di puntatore, viene espanso usando i cicli for annidati come segue: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
    }
}

Le variabili a, i0, i1,..., iN non sono visibili o accessibili a x o embedded_statement o qualsiasi altro codice sorgente del programma.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 variabile v è di sola lettura nell'istruzione incorporata.The variable v is read-only in the embedded statement. Se non è presente una conversione esplicita (conversioni di puntatore) da T (il tipo di elemento) a V, viene generato un errore e non vengono eseguiti altri passaggi.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. Se x ha il valore null, viene generata un'System.NullReferenceException in fase di esecuzione.If x has the value null, a System.NullReferenceException is thrown at run-time.

Puntatori nelle espressioniPointers in expressions

In un contesto unsafe, un'espressione può restituire un risultato di un tipo di puntatore, ma all'esterno di un contesto non sicuro si tratta di un errore in fase di compilazione perché un'espressione sia di tipo puntatore.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. In termini precisi, all'esterno di un contesto non sicuro si verifica un errore in fase di compilazione se simple_name (nomi semplici), member_access (accesso ai membri), invocation_expression (espressioni di chiamata) o element_access (accesso agli elementi) è di tipo puntatore.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.

In un contesto non sicuro, le produzioni primary_no_array_creation_expression (espressioni primarie) e unary_expression (operatori unari) consentono i seguenti costrutti aggiuntivi: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
    ;

Questi costrutti sono descritti nelle sezioni riportate di seguito.These constructs are described in the following sections. La precedenza e l'associatività degli operatori unsafe sono implicite nella grammatica.The precedence and associativity of the unsafe operators is implied by the grammar.

Riferimento indiretto del puntatorePointer indirection

Un pointer_indirection_expression è costituito da un asterisco (*) seguito da un unary_expression.A pointer_indirection_expression consists of an asterisk (*) followed by a unary_expression.

pointer_indirection_expression
    : '*' unary_expression
    ;

L'operatore unario * denota il riferimento indiretto del puntatore e viene usato per ottenere la variabile a cui punta un puntatore.The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. Risultato della valutazione di *P, in cui P è un'espressione di un tipo di puntatore T*, è una variabile di tipo T.The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. Si tratta di un errore in fase di compilazione per applicare l'operatore unario * a un'espressione di tipo void* o a un'espressione che non è di tipo puntatore.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'effetto dell'applicazione dell'operatore unario * a un puntatore null è definito dall'implementazione.The effect of applying the unary * operator to a null pointer is implementation-defined. In particolare, non esiste alcuna garanzia che questa operazione generi un System.NullReferenceException.In particular, there is no guarantee that this operation throws a System.NullReferenceException.

Se al puntatore è stato assegnato un valore non valido, il comportamento dell'operatore unario * non è definito.If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. Tra i valori non validi per la dereferenziazione di un puntatore dall'operatore unario * si trovano un indirizzo allineato in modo non appropriato per il tipo a cui punta (vedere l'esempio nelle conversioni dei puntatori) e l'indirizzo di una variabile dopo la fine della sua durata.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.

Ai fini dell'analisi di assegnazione definita, una variabile prodotta dalla valutazione di un'espressione nel formato *P viene considerata inizialmente assegnata (variabili assegnate inizialmente).For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned (Initially assigned variables).

Accesso ai membri puntatorePointer member access

Un pointer_member_access è costituito da un primary_expression, seguito da un token "->", seguito da un identificatore e da un type_argument_listfacoltativo.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
    ;

In un accesso al membro del puntatore nel formato P->I, P deve essere un'espressione di un tipo di puntatore diverso da void* e I deve indicare un membro accessibile del tipo a cui P punti.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 accesso al membro del puntatore del modulo P->I viene valutato esattamente come (*P).I.A pointer member access of the form P->I is evaluated exactly as (*P).I. Per una descrizione dell'operatore di riferimento indiretto del puntatore (*), vedere riferimento indiretto del puntatore.For a description of the pointer indirection operator (*), see Pointer indirection. Per una descrizione dell'operatore di accesso ai membri (.), vedere accesso ai membri.For a description of the member access operator (.), see Member access.

Nell'esempioIn 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'operatore -> viene usato per accedere ai campi e richiamare un metodo di uno struct tramite un puntatore.the -> operator is used to access fields and invoke a method of a struct through a pointer. Poiché l'operazione P->I è esattamente equivalente a (*P).I, è possibile che sia stato scritto anche il metodo Main: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());
        }
    }
}

Accesso agli elementi del puntatorePointer element access

Un pointer_element_access è costituito da un primary_no_array_creation_expression seguito da un'espressione racchiusa tra "[" e "]".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 ']'
    ;

Nell'accesso a un elemento del puntatore nel formato P[E], P deve essere un'espressione di un tipo di puntatore diverso da void* e E deve essere un'espressione che può essere convertita in modo implicito in int, uint, long o 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.

L'accesso a un elemento puntatore del modulo P[E] viene valutato esattamente come *(P + E).A pointer element access of the form P[E] is evaluated exactly as *(P + E). Per una descrizione dell'operatore di riferimento indiretto del puntatore (*), vedere riferimento indiretto del puntatore.For a description of the pointer indirection operator (*), see Pointer indirection. Per una descrizione dell'operatore di addizione del puntatore (+), vedere aritmetica dei puntatori.For a description of the pointer addition operator (+), see Pointer arithmetic.

Nell'esempioIn the example

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

l'accesso a un elemento del puntatore viene usato per inizializzare il buffer di caratteri in un ciclo for.a pointer element access is used to initialize the character buffer in a for loop. Poiché l'operazione P[E] è esattamente equivalente a *(P + E), è possibile che sia stato scritto anche l'esempio seguente: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'operatore di accesso dell'elemento puntatore non verifica la presenza di errori fuori limite e il comportamento quando si accede a un elemento fuori limite non è definito.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. Corrisponde a C e C++.This is the same as C and C++.

Operatore address-ofThe address-of operator

Un addressof_expression è costituito da una e commerciale (&) seguita da un unary_expression.An addressof_expression consists of an ampersand (&) followed by a unary_expression.

addressof_expression
    : '&' unary_expression
    ;

Data un'espressione E che è di un tipo T ed è classificata come variabile fissa (variabili fisse e mobili), il costrutto &E calcola l'indirizzo della variabile fornita da 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. Il tipo del risultato è T* ed è classificato come valore.The type of the result is T* and is classified as a value. Si verifica un errore in fase di compilazione se E non è classificato come variabile, se E è classificato come variabile locale di sola lettura o se E denota una variabile mobile.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. Nell'ultimo caso, è possibile usare un'istruzione fixed (istruzione fixed) per correggere temporaneamente la variabile prima di ottenerne l'indirizzo.In the last case, a fixed statement (The fixed statement) can be used to temporarily "fix" the variable before obtaining its address. Come indicato nell' accesso ai membri, al di fuori di un costruttore di istanza o di un costruttore statico per uno struct o una classe che definisce un campo readonly, tale campo viene considerato un valore, non una variabile.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. Di conseguenza, non è possibile eseguire il relativo indirizzo.As such, its address cannot be taken. Analogamente, non è possibile eseguire l'indirizzo di una costante.Similarly, the address of a constant cannot be taken.

L'operatore & non richiede che il relativo argomento venga assegnato definitivamente, ma dopo un'operazione &, la variabile a cui viene applicato l'operatore viene considerata definitivamente assegnata nel percorso di esecuzione in cui si verifica l'operazione.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. È responsabilità del programmatore garantire che la corretta inizializzazione della variabile venga effettivamente eseguita in questa situazione.It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

Nell'esempioIn the example

using System;

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

i viene considerato definitivamente assegnato dopo l'operazione &i usata per inizializzare p.i is considered definitely assigned following the &i operation used to initialize p. L'assegnazione a *p in effetti Inizializza i, ma l'inclusione di questa inizializzazione è responsabilità del programmatore e non si verificherà alcun errore in fase di compilazione se l'assegnazione è stata rimossa.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.

Sono presenti regole di assegnazione definitiva per l'operatore & in modo che sia possibile evitare l'inizializzazione ridondante delle variabili locali.The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. Molte API esterne, ad esempio, accettano un puntatore a una struttura compilata dall'API.For example, many external APIs take a pointer to a structure which is filled in by the API. Le chiamate a tali API in genere passano l'indirizzo di una variabile di struct locale e senza la regola è necessaria l'inizializzazione ridondante della variabile struct.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.

Incremento e decremento dei puntatoriPointer increment and decrement

In un contesto non sicuro, è possibile applicare gli operatori ++ e -- (operatori di incremento e decremento suffisso e operatori di incremento e decremento del prefisso) a variabili puntatore di tutti i tipi ad eccezione di 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*. Pertanto, per ogni tipo di puntatore T*, gli operatori seguenti sono definiti in modo implicito:Thus, for every pointer type T*, the following operators are implicitly defined:

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

Gli operatori producono gli stessi risultati rispettivamente x + 1 e x - 1 (aritmetica del puntatore).The operators produce the same results as x + 1 and x - 1, respectively (Pointer arithmetic). In altre parole, per una variabile puntatore di tipo T*, l'operatore ++ aggiunge sizeof(T) all'indirizzo contenuto nella variabile e l'operatore -- sottrae sizeof(T) dall'indirizzo contenuto nella variabile.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.

Se un'operazione di incremento o di decremento di un puntatore causa un overflow del dominio del tipo di puntatore, il risultato è definito dall'implementazione, ma non vengono generate eccezioni.If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

Puntatore aritmeticoPointer arithmetic

In un contesto non sicuro, è possibile applicare gli operatori + e - (operatore diaddizione e sottrazione) a valori di tutti i tipi di puntatore eccetto void*.In an unsafe context, the + and - operators (Addition operator and Subtraction operator) can be applied to values of all pointer types except void*. Pertanto, per ogni tipo di puntatore T*, gli operatori seguenti sono definiti in modo implicito: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);

Data un'espressione P di un tipo di puntatore T* e un'espressione N di tipo int, uint, long o ulong, le espressioni P + N e N + P calcolano il valore del puntatore di tipo T* risultante dall'aggiunta di 0 all'indirizzo fornito da 1.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. Analogamente, l'espressione P - N calcola il valore del puntatore di tipo T* risultante dalla sottrazione di N * sizeof(T) dall'indirizzo fornito da 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.

Date due espressioni, P e Q, di un tipo di puntatore T*, l'espressione P - Q calcola la differenza tra gli indirizzi specificati da P e Q e quindi divide tale differenza per 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). Il tipo del risultato è sempre long.The type of the result is always long. In effetti, P - Q viene calcolato come ((long)(P) - (long)(Q)) / sizeof(T).In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

Esempio: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);
        }
    }
}

che produce l'output:which produces the output:

p - q = -14
q - p = 14

Se un'operazione aritmetica del puntatore causa un overflow del dominio del tipo di puntatore, il risultato viene troncato in modo definito dall'implementazione, ma non vengono generate eccezioni.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.

Confronto tra puntatoriPointer comparison

In un contesto non sicuro, è possibile applicare gli operatori ==, !=, <, >, <= e => (operatori relazionali e di test dei tipi) a valori di tutti i tipi di puntatore.In an unsafe context, the ==, !=, <, >, <=, and => operators (Relational and type-testing operators) can be applied to values of all pointer types. Gli operatori di confronto del puntatore sono: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);

Poiché esiste una conversione implicita da qualsiasi tipo di puntatore al tipo void*, gli operandi di qualsiasi tipo di puntatore possono essere confrontati usando questi operatori.Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. Gli operatori di confronto confrontano gli indirizzi forniti dai due operandi come se fossero interi senza segno.The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

Operatore sizeofThe sizeof operator

L'operatore sizeof restituisce il numero di byte occupati da una variabile di un tipo specificato.The sizeof operator returns the number of bytes occupied by a variable of a given type. Il tipo specificato come operando per sizeof deve essere un unmanaged_type (tipi puntatore).The type specified as an operand to sizeof must be an unmanaged_type (Pointer types).

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

Il risultato dell'operatore sizeof è un valore di tipo int.The result of the sizeof operator is a value of type int. Per determinati tipi predefiniti, l'operatore sizeof produce un valore costante, come illustrato nella tabella seguente.For certain predefined types, the sizeof operator yields a constant value as shown in the table below.

EspressioneExpression RisultatoResult
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

Per tutti gli altri tipi, il risultato dell'operatore sizeof è definito dall'implementazione ed è classificato come valore, non come costante.For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

L'ordine in cui i membri vengono compressi in uno struct non è specificato.The order in which members are packed into a struct is unspecified.

Ai fini dell'allineamento, potrebbe essere presente una spaziatura senza nome all'inizio di uno struct, all'interno di uno struct e alla fine dello 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. Il contenuto dei bit utilizzati come spaziatura interna è indeterminato.The contents of the bits used as padding are indeterminate.

Quando viene applicato a un operando con tipo struct, il risultato è il numero totale di byte in una variabile di quel tipo, inclusa la spaziatura interna.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.

Istruzione fixedThe fixed statement

In un contesto non sicuro, la produzione embedded_statement (statements) consente un costrutto aggiuntivo, l'istruzione fixed, che viene usata per "correggere" una variabile mobile in modo che l'indirizzo rimanga costante per la durata dell'istruzione .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
    ;

Ogni fixed_pointer_declarator dichiara una variabile locale del pointer_type specificato e inizializza la variabile locale con l'indirizzo calcolato dal fixed_pointer_initializercorrispondente.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. Una variabile locale dichiarata in un'istruzione fixed è accessibile in qualsiasi fixed_pointer_initializerche si verifica a destra della dichiarazione della variabile e in embedded_statement dell'istruzione 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. Una variabile locale dichiarata da un'istruzione fixed è considerata di sola lettura.A local variable declared by a fixed statement is considered read-only. Si verifica un errore in fase di compilazione se l'istruzione incorporata tenta di modificare questa variabile locale (tramite assegnazione o gli operatori ++ e --) o di passarla come parametro ref o 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 può essere uno dei seguenti:A fixed_pointer_initializer can be one of the following:

  • Il token "&" seguito da un variable_reference (regole precise per determinare l'assegnazione definita) a una variabile mobile (variabili fisse e mobili) di un tipo non gestito T, purché il tipo T* sia convertibile in modo implicito nel tipo di puntatore specificato nell'istruzione 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. In questo caso, l'inizializzatore calcola l'indirizzo della variabile specificata e la variabile rimane in un indirizzo fisso per la durata dell'istruzione 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.
  • Espressione di un array_type con elementi di un tipo non gestito T, a condizione che il tipo T* sia convertibile in modo implicito nel tipo di puntatore specificato nell'istruzione 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. In questo caso, l'inizializzatore calcola l'indirizzo del primo elemento nella matrice e l'intera matrice rimane in corrispondenza di un indirizzo fisso per la durata dell'istruzione 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. Se l'espressione di matrice è null o se la matrice ha zero elementi, l'inizializzatore calcola un indirizzo uguale a zero.If the array expression is null or if the array has zero elements, the initializer computes an address equal to zero.
  • Espressione di tipo string, a condizione che il tipo char* sia convertibile in modo implicito nel tipo di puntatore specificato nell'istruzione fixed.An expression of type string, provided the type char* is implicitly convertible to the pointer type given in the fixed statement. In questo caso, l'inizializzatore calcola l'indirizzo del primo carattere nella stringa e l'intera stringa rimane in un indirizzo fisso per la durata dell'istruzione 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. Il comportamento dell'istruzione fixed è definito dall'implementazione se l'espressione stringa è null.The behavior of the fixed statement is implementation-defined if the string expression is null.
  • Simple_name o member_access che fa riferimento a un membro del buffer a dimensione fissa di una variabile mobile, purché il tipo del membro del buffer a dimensione fissa sia convertibile in modo implicito nel tipo di puntatore specificato nell'istruzione 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. In questo caso, l'inizializzatore calcola un puntatore al primo elemento del buffer a dimensione fissa (buffer a dimensione fissa nelle espressioni) e il buffer a dimensione fissa rimane in un indirizzo fisso per la durata dell'istruzione 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.

Per ogni indirizzo calcolato da un fixed_pointer_initializer , l'istruzione fixed garantisce che la variabile a cui fa riferimento l'indirizzo non sia soggetta alla rilocazione o all'eliminazione da parte del Garbage Collector per la durata dell'istruzione 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. Se, ad esempio, l'indirizzo calcolato da un fixed_pointer_initializer fa riferimento a un campo di un oggetto o di un elemento di un'istanza di matrice, l'istruzione fixed garantisce che l'istanza dell'oggetto contenitore non venga rilocata o eliminata durante la durata dell'istruzione.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.

È responsabilità del programmatore garantire che i puntatori creati dalle istruzioni fixed non sopravvivano oltre l'esecuzione di tali istruzioni.It is the programmer's responsibility to ensure that pointers created by fixed statements do not survive beyond execution of those statements. Se, ad esempio, i puntatori creati dalle istruzioni fixed vengono passati alle API esterne, è responsabilità del programmatore garantire che le API non mantengano memoria di questi puntatori.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.

Gli oggetti fissi possono causare la frammentazione dell'heap, perché non possono essere spostati.Fixed objects may cause fragmentation of the heap (because they can't be moved). Per questo motivo, gli oggetti devono essere corretti solo quando sono assolutamente necessari e quindi solo per la quantità di tempo più breve possibile.For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible.

EsempioThe 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);
        }
    }
}

vengono illustrati diversi utilizzi dell'istruzione fixed.demonstrates several uses of the fixed statement. La prima istruzione corregge e ottiene l'indirizzo di un campo statico, la seconda corregge e ottiene l'indirizzo di un campo di istanza e la terza istruzione corregge e ottiene l'indirizzo di un elemento di matrice.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. In ogni caso sarebbe stato un errore usare l'operatore normale & poiché le variabili sono tutte classificate come variabili mobili.In each case it would have been an error to use the regular & operator since the variables are all classified as moveable variables.

La quarta istruzione fixed nell'esempio precedente produce un risultato simile al terzo.The fourth fixed statement in the example above produces a similar result to the third.

Questo esempio dell'istruzione fixed USA 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);
        }
    }
}

In una matrice di contesto unsafe gli elementi delle matrici unidimensionali vengono archiviati in ordine di indice crescente, iniziando con index 0 e terminando con 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. Per le matrici multidimensionali, gli elementi di matrice vengono archiviati in modo che gli indici della dimensione più a destra vengano aumentati per primi, quindi la dimensione a sinistra successiva e così via verso sinistra.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. All'interno di un'istruzione fixed che ottiene un puntatore p a un'istanza di matrice a, i valori del puntatore compresi tra p e p + a.Length - 1 rappresentano gli indirizzi degli elementi nella matrice.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. Analogamente, le variabili comprese tra p[0] e p[a.Length - 1] rappresentano gli elementi della matrice effettivi.Likewise, the variables ranging from p[0] to p[a.Length - 1] represent the actual array elements. Dato il modo in cui vengono archiviate le matrici, è possibile considerare una matrice di qualsiasi dimensione come se fosse lineare.Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear.

Esempio: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();
            }
    }
}

che produce l'output: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

Nell'esempioIn 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);
        }
    }
}

viene utilizzata un'istruzione fixed per correggere una matrice in modo che l'indirizzo possa essere passato a un metodo che accetta un puntatore.a fixed statement is used to fix an array so its address can be passed to a method that takes a pointer.

Nell’esempioIn 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);
        }
    }
}

viene utilizzata un'istruzione fixed per correggere un buffer a dimensione fissa di uno struct, in modo che l'indirizzo possa essere utilizzato come puntatore.a fixed statement is used to fix a fixed size buffer of a struct so its address can be used as a pointer.

Un valore char* prodotto mediante la correzione di un'istanza di stringa fa sempre riferimento a una stringa con terminazione null.A char* value produced by fixing a string instance always points to a null-terminated string. All'interno di un'istruzione fixed che ottiene un puntatore p a un'istanza di stringa s, i valori del puntatore che variano da p a p + s.Length - 1 rappresentano gli indirizzi dei caratteri nella stringa e il valore dell'indicatore di misura p + s.Length fa sempre riferimento a un carattere null (il carattere con valore '\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 modifica di oggetti di tipo gestito tramite puntatori fissi può comportare un comportamento non definito.Modifying objects of managed type through fixed pointers can results in undefined behavior. Poiché, ad esempio, le stringhe non sono modificabili, è responsabilità del programmatore garantire che i caratteri a cui fa riferimento un puntatore a una stringa fissa non vengano modificati.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.

La terminazione null automatica delle stringhe è particolarmente utile quando si chiamano API esterne che prevedono stringhe di tipo "C".The automatic null-termination of strings is particularly convenient when calling external APIs that expect "C-style" strings. Si noti, tuttavia, che un'istanza di stringa può contenere caratteri null.Note, however, that a string instance is permitted to contain null characters. Se sono presenti caratteri null, la stringa viene visualizzata troncata quando viene considerata come un char* con terminazione null.If such null characters are present, the string will appear truncated when treated as a null-terminated char*.

Buffer a dimensione fissaFixed size buffers

I buffer a dimensione fissa vengono usati per dichiarare le matrici inline "in stile C" come membri degli struct e sono particolarmente utili per l'interazione con le API non gestite.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.

Dichiarazioni di buffer a dimensione fissaFixed size buffer declarations

Un buffer a dimensione fissa è un membro che rappresenta lo spazio di archiviazione per un buffer a lunghezza fissa di variabili di un determinato tipo.A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. Una dichiarazione di buffer a dimensione fissa introduce uno o più buffer a dimensione fissa di un determinato tipo di elemento.A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. I buffer a dimensione fissa sono consentiti solo nelle dichiarazioni struct e possono verificarsi solo in contesti non sicuri (contesti non sicuri).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 ']'
    ;

Una dichiarazione di buffer a dimensione fissa può includere un set di attributi (attributi), un modificatore new (modificatori), una combinazione valida dei quattro modificatori di accesso (parametri di tipo e vincoli) e un modificatore unsafe (nonsicuro) contesti).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). Gli attributi e i modificatori si applicano a tutti i membri dichiarati dalla dichiarazione del buffer a dimensione fissa.The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. È un errore che lo stesso modificatore venga visualizzato più volte in una dichiarazione di buffer a dimensione fissa.It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.

Una dichiarazione di buffer a dimensione fissa non è consentita per includere il modificatore static.A fixed size buffer declaration is not permitted to include the static modifier.

Il tipo di elemento buffer di una dichiarazione di buffer a dimensione fissa specifica il tipo di elemento dei buffer introdotti dalla dichiarazione.The buffer element type of a fixed size buffer declaration specifies the element type of the buffer(s) introduced by the declaration. Il tipo di elemento del buffer deve essere uno dei tipi predefiniti sbyte, byte, short, ushort, int, uint, long, ulong, char, float, 0 o 1.The buffer element type must be one of the predefined types sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or bool.

Il tipo di elemento del buffer è seguito da un elenco di dichiaratori di buffer a dimensione fissa, ciascuno dei quali introduce un nuovo membro.The buffer element type is followed by a list of fixed size buffer declarators, each of which introduces a new member. Un dichiaratore di buffer a dimensione fissa è costituito da un identificatore che assegna un nome al membro, seguito da un'espressione costante racchiusa tra token [ e ].A fixed size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in [ and ] tokens. L'espressione costante indica il numero di elementi nel membro introdotto dal dichiaratore di buffer a dimensione fissa.The constant expression denotes the number of elements in the member introduced by that fixed size buffer declarator. Il tipo dell'espressione costante deve essere convertibile in modo implicito nel tipo int e il valore deve essere un numero intero positivo diverso da zero.The type of the constant expression must be implicitly convertible to type int, and the value must be a non-zero positive integer.

Gli elementi di un buffer a dimensione fissa sono sicuramente disposti in sequenza in memoria.The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.

Una dichiarazione di buffer a dimensione fissa che dichiara più buffer a dimensione fissa equivale a più dichiarazioni di una singola dichiarazione di buffer a dimensione fissa con gli stessi attributi e tipi di elemento.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. Esempio:For example

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

equivale ais equivalent to

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

Buffer a dimensione fissa nelle espressioniFixed size buffers in expressions

La ricerca di membri (operatori) di un membro del buffer a dimensione fissa procede esattamente come la ricerca dei membri di un campo.Member lookup (Operators) of a fixed size buffer member proceeds exactly like member lookup of a field.

È possibile fare riferimento a un buffer a dimensione fissa in un'espressione mediante simple_name (inferenza del tipo) o member_access (controllo della fase di compilazione della risoluzione dell'overload dinamico).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).

Quando si fa riferimento a un membro del buffer a dimensione fissa come nome semplice, l'effetto è identico a quello di un accesso ai membri nel formato this.I, dove I è il membro del buffer a dimensione fissa.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.

In un accesso ai membri nel formato E.I, se E è di tipo struct e una ricerca di membri di I in quel tipo di struct identifica un membro a dimensione fissa, E.I viene valutato come indicato di seguito: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:

  • Se l'espressione E.I non viene eseguita in un contesto non sicuro, si verifica un errore in fase di compilazione.If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
  • Se E è classificato come valore, si verifica un errore in fase di compilazione.If E is classified as a value, a compile-time error occurs.
  • In caso contrario, se E è una variabile mobile (variabili fisse e mobili) e l'espressione E.I non è un fixed_pointer_initializer (l'istruzione fixed), si verificherà un errore in fase di compilazione.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.
  • In caso contrario, E fa riferimento a una variabile fissa e il risultato dell'espressione è un puntatore al primo elemento del membro del buffer a dimensione fissa I in 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. Il risultato è di tipo S*, dove S è il tipo di elemento di I ed è classificato come valore.The result is of type S*, where S is the element type of I, and is classified as a value.

È possibile accedere agli elementi successivi del buffer a dimensione fissa usando le operazioni del puntatore dal primo elemento.The subsequent elements of the fixed size buffer can be accessed using pointer operations from the first element. A differenza dell'accesso alle matrici, l'accesso agli elementi di un buffer a dimensione fissa è un'operazione non sicura e non è selezionato intervallo.Unlike access to arrays, access to the elements of a fixed size buffer is an unsafe operation and is not range checked.

Nell'esempio seguente viene dichiarato e utilizzato uno struct con un membro del buffer a dimensione fissa.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);
    }
}

Controllo di assegnazione definitoDefinite assignment checking

I buffer a dimensione fissa non sono soggetti al controllo di assegnazione definito (assegnazione definita) e i membri del buffer a dimensione fissa vengono ignorati ai fini del controllo di assegnazione definito delle variabili di tipo 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.

Quando la variabile di struct contenitore più esterna di un membro del buffer a dimensione fissa è una variabile statica, una variabile di istanza di un'istanza di classe o un elemento di matrice, gli elementi del buffer a dimensione fissa vengono inizializzati automaticamente sui valori predefiniti (impostazione predefinita valori).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). In tutti gli altri casi, il contenuto iniziale di un buffer a dimensione fissa non è definito.In all other cases, the initial content of a fixed size buffer is undefined.

Allocazione dello stackStack allocation

In un contesto unsafe, una dichiarazione di variabile locale (dichiarazioni di variabili locali) può includere un inizializzatore di allocazione dello stack che alloca memoria dallo stack di chiamate.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 ']'
    ;

Unmanaged_type indica il tipo degli elementi che verranno archiviati nel percorso appena allocato e l' espressione indica il numero di questi elementi.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. Insieme, questi specificano le dimensioni di allocazione richieste.Taken together, these specify the required allocation size. Poiché le dimensioni di un'allocazione dello stack non possono essere negative, si verifica un errore in fase di compilazione per specificare il numero di elementi come constant_expression che restituisce un valore negativo.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.

Per un inizializzatore di allocazione dello stack nel formato stackalloc T[E] è necessario che T sia un tipo non gestito (tipi puntatore) e E come espressione di tipo 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. Il costrutto alloca E * sizeof(T) byte dallo stack di chiamate e restituisce un puntatore di tipo T* al nuovo blocco allocato.The construct allocates E * sizeof(T) bytes from the call stack and returns a pointer, of type T*, to the newly allocated block. Se E è un valore negativo, il comportamento non è definito.If E is a negative value, then the behavior is undefined. Se E è zero, non viene eseguita alcuna allocazione e il puntatore restituito è definito dall'implementazione.If E is zero, then no allocation is made, and the pointer returned is implementation-defined. Se non è disponibile memoria sufficiente per allocare un blocco della dimensione specificata, viene generata un'System.StackOverflowException.If there is not enough memory available to allocate a block of the given size, a System.StackOverflowException is thrown.

Il contenuto della memoria appena allocata non è definito.The content of the newly allocated memory is undefined.

Gli inizializzatori di allocazione dello stack non sono consentiti nei blocchi catch o finally (istruzione try).Stack allocation initializers are not permitted in catch or finally blocks (The try statement).

Non è possibile liberare esplicitamente la memoria allocata utilizzando stackalloc.There is no way to explicitly free memory allocated using stackalloc. Tutti i blocchi di memoria allocati dello stack creati durante l'esecuzione di un membro di funzione vengono eliminati automaticamente quando il membro della funzione restituisce.All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. Corrisponde alla funzione alloca, un'estensione che si trova comunemente nelle implementazioni C++ di C e.This corresponds to the alloca function, an extension commonly found in C and C++ implementations.

Nell'esempioIn 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 inizializzatore stackalloc viene usato nel metodo IntToString per allocare un buffer di 16 caratteri nello stack.a stackalloc initializer is used in the IntToString method to allocate a buffer of 16 characters on the stack. Il buffer viene rimosso automaticamente quando il metodo restituisce un risultato.The buffer is automatically discarded when the method returns.

Allocazione di memoria dinamicaDynamic memory allocation

Ad eccezione dell'operatore stackalloc, C# non fornisce costrutti predefiniti per la gestione della memoria non sottoposta a Garbage Collection.Except for the stackalloc operator, C# provides no predefined constructs for managing non-garbage collected memory. Tali servizi vengono in genere forniti mediante il supporto di librerie di classi o importati direttamente dal sistema operativo sottostante.Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. Ad esempio, nella classe Memory riportata di seguito viene illustrato il modo in C#cui è possibile accedere alle funzioni dell'heap di un sistema operativo sottostante: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);
}

Di seguito è riportato un esempio che usa 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);
        }
    }
}

Nell'esempio vengono allocati 256 byte di memoria tramite Memory.Alloc e viene inizializzato il blocco di memoria con valori che aumentano da 0 a 255.The example allocates 256 bytes of memory through Memory.Alloc and initializes the memory block with values increasing from 0 to 255. Viene quindi allocata una matrice di byte di elementi 256 e viene utilizzato Memory.Copy per copiare il contenuto del blocco di memoria nella matrice di byte.It then allocates a 256 element byte array and uses Memory.Copy to copy the contents of the memory block into the byte array. Infine, il blocco di memoria viene liberato utilizzando Memory.Free e il contenuto della matrice di byte viene restituito nella console.Finally, the memory block is freed using Memory.Free and the contents of the byte array are output on the console.