Código não seguroUnsafe code

A linguagem C# principal, conforme definido nos capítulos anteriores, difere notavelmente de C e C++ em sua omissão de ponteiros como um tipo de dados.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. Em vez disso, o C# fornece referências e a capacidade de criar objetos gerenciados por um coletor de lixo.Instead, C# provides references and the ability to create objects that are managed by a garbage collector. Esse design, juntamente com outros recursos, torna o C# uma linguagem muito mais segura do que C ou C++.This design, coupled with other features, makes C# a much safer language than C or C++. Na linguagem C# principal, simplesmente não é possível ter uma variável não inicializada, um ponteiro "pendente" ou uma expressão que indexe uma matriz além de seus 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. Categorias inteiras de bugs que, rotineiramente, os programas C e C++ são eliminados.Whole categories of bugs that routinely plague C and C++ programs are thus eliminated.

Embora praticamente todas as construções de tipo de ponteiro em C ou C++ tenham um tipo de referência equivalente em C#, no entanto, há situações em que o acesso a tipos de ponteiro se torna uma necessidade.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. Por exemplo, a interface com o sistema operacional subjacente, o acesso a um dispositivo mapeado por memória ou a implementação de um algoritmo de tempo crítico pode não ser possível ou prática sem acesso a ponteiros.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. Para atender a essa necessidade, o C# fornece a capacidade de escrever código não seguro.To address this need, C# provides the ability to write unsafe code.

Em código não seguro, é possível declarar e operar em ponteiros, executar conversões entre ponteiros e tipos integrais, para obter o endereço de variáveis e assim por diante.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. De certa forma, escrever código inseguro é muito parecido com escrever código C em um programa em C#.In a sense, writing unsafe code is much like writing C code within a C# program.

O código não seguro é, de fato, um recurso "seguro" da perspectiva de desenvolvedores e usuários.Unsafe code is in fact a "safe" feature from the perspective of both developers and users. O código não seguro deve ser claramente marcado com o modificador unsafe , de modo que os desenvolvedores não podem possivelmente usar recursos não seguros acidentalmente, e o mecanismo de execução funciona para garantir que o código não seguro não possa ser executado em um ambiente não confiável.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.

Contextos não segurosUnsafe contexts

Os recursos não seguros do C# estão disponíveis apenas em contextos não seguros.The unsafe features of C# are available only in unsafe contexts. Um contexto não seguro é introduzido por meio da inclusão de um unsafe modificador na declaração de um tipo ou membro, ou ao empregar um 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:

  • Uma declaração de Class, struct, interface ou delegate pode incluir um unsafe modificador; nesse caso, toda a extensão textual dessa declaração de tipo (incluindo o corpo da classe, struct ou interface) é considerada um contexto não seguro.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.
  • Uma declaração de um campo, método, propriedade, evento, indexador, operador, Construtor de instância, destruidor ou construtor estático pode incluir um unsafe modificador; nesse caso, toda a extensão textual dessa declaração de membro é considerada um contexto não seguro.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.
  • Um unsafe_statement permite o uso de um contexto sem segurança dentro de um bloco.An unsafe_statement enables the use of an unsafe context within a block. Toda a extensão textual do bloco associado é considerada um contexto não seguro.The entire textual extent of the associated block is considered an unsafe context.

As produções gramaticais associadas são mostradas abaixo.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
    ;

No exemploIn the example

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

o unsafe modificador especificado na declaração struct faz com que toda a extensão textual da declaração struct se torne um contexto não seguro.the unsafe modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. Portanto, é possível declarar os Left Right campos e como sendo de um tipo de ponteiro.Thus, it is possible to declare the Left and Right fields to be of a pointer type. O exemplo acima também poderia ser escritoThe example above could also be written

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

Aqui, os unsafe modificadores nas declarações de campo fazem com que essas declarações sejam consideradas contextos não seguros.Here, the unsafe modifiers in the field declarations cause those declarations to be considered unsafe contexts.

Além de estabelecer um contexto não seguro, permitindo, portanto, o uso de tipos de ponteiro, o unsafe modificador não tem nenhum efeito sobre um tipo ou um 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. No exemploIn the example

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

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

o unsafe modificador no F método em A simplesmente faz com que a extensão textual F se torne um contexto não seguro no qual os recursos não seguros do idioma podem ser usados.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. Na substituição de F em, não há B necessidade de especificar novamente o unsafe modificador, a menos que, é claro, o F método em B si precise de acesso a recursos não seguros.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.

A situação é um pouco diferente quando um tipo de ponteiro faz parte da assinatura do métodoThe 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) {...}
}

Aqui, como F a assinatura do inclui um tipo de ponteiro, ela só pode ser gravada em um contexto sem segurança.Here, because F's signature includes a pointer type, it can only be written in an unsafe context. No entanto, o contexto não seguro pode ser introduzido, tornando a classe inteira insegura, como é o caso no A , ou incluindo um unsafe modificador na declaração do método, como é o caso no 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.

Tipos de ponteiroPointer types

Em um contexto não seguro, um tipo (tipos) pode ser um pointer_type , bem como um value_type ou um reference_type.In an unsafe context, a type (Types) may be a pointer_type as well as a value_type or a reference_type. No entanto, um pointer_type também pode ser usado em uma typeof expressão (expressões de criação de objeto anônimo) fora de um contexto sem segurança, pois tal uso não é seguro.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
    ;

Uma pointer_type é escrita como uma unmanaged_type ou a palavra-chave void , seguida por um * 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
    ;

O tipo especificado antes do * em um tipo de ponteiro é chamado de tipo Referent do tipo de ponteiro.The type specified before the * in a pointer type is called the referent type of the pointer type. Representa o tipo da variável para a qual um valor do tipo de ponteiro aponta.It represents the type of the variable to which a value of the pointer type points.

Ao contrário das referências (valores de tipos de referência), os ponteiros não são rastreados pelo coletor de lixo – o coletor de lixo não tem conhecimento de ponteiros e dos dados para os quais eles apontam.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. Por esse motivo, um ponteiro não tem permissão para apontar para uma referência ou para uma struct que contém referências, e o tipo Referent de um ponteiro deve ser um 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.

Um unmanaged_type é qualquer tipo que não seja um tipo reference_type ou construído e não contenha reference_type ou campos de tipo construído em qualquer nível de aninhamento.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. Em outras palavras, um unmanaged_type é um dos seguintes:In other words, an unmanaged_type is one of the following:

  • sbyte,,,,,, byte short ushort int uint long , ulong , char , float , double , decimal ou bool .sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Qualquer enum_type.Any enum_type.
  • Qualquer pointer_type.Any pointer_type.
  • Qualquer struct_type definida pelo usuário que não seja um tipo construído e contém campos somente unmanaged_type s.Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_type s only.

A regra intuitiva para a combinação de ponteiros e referências é que referents de referências (objetos) têm permissão para conter ponteiros, mas referents de ponteiros não tem permissão para conter referências.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.

Alguns exemplos de tipos de ponteiro são fornecidos na tabela a seguir:Some examples of pointer types are given in the table below:

ExemploExample DescriçãoDescription
byte* Ponteiro para bytePointer to byte
char* Ponteiro para charPointer to char
int** Ponteiro para ponteiro para intPointer to pointer to int
int*[] Matriz unidimensional de ponteiros para intSingle-dimensional array of pointers to int
void* Ponteiro para tipo desconhecidoPointer to unknown type

Para uma determinada implementação, todos os tipos de ponteiro devem ter o mesmo tamanho e representação.For a given implementation, all pointer types must have the same size and representation.

Ao contrário de C e C++, quando vários ponteiros são declarados na mesma declaração, em C#, o * é escrito junto com o tipo subjacente apenas, não como um pontuador de prefixo em cada nome de ponteiro.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. Por exemplo,For example

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

O valor de um ponteiro com tipo T* representa o endereço de uma variável do tipo T .The value of a pointer having type T* represents the address of a variable of type T. O operador de indireção de ponteiro * (indireção de ponteiro) pode ser usado para acessar essa variável.The pointer indirection operator * (Pointer indirection) may be used to access this variable. Por exemplo, dada uma variável P do tipo int* , a expressão *P denota a int variável encontrada no endereço contido em P .For example, given a variable P of type int*, the expression *P denotes the int variable found at the address contained in P.

Como uma referência de objeto, um ponteiro pode ser null .Like an object reference, a pointer may be null. A aplicação do operador de indireção a um null ponteiro resulta em comportamento definido pela implementação.Applying the indirection operator to a null pointer results in implementation-defined behavior. Um ponteiro com valor null é representado por todos os bits-zero.A pointer with value null is represented by all-bits-zero.

O void* tipo representa um ponteiro para um tipo desconhecido.The void* type represents a pointer to an unknown type. Como o tipo Referent é desconhecido, o operador de indireção não pode ser aplicado a um ponteiro do tipo void* , nem qualquer aritmética pode ser executada nesse ponteiro.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. No entanto, um ponteiro do tipo void* pode ser convertido em qualquer outro tipo de ponteiro (e vice-versa).However, a pointer of type void* can be cast to any other pointer type (and vice versa).

Os tipos de ponteiro são uma categoria separada de tipos.Pointer types are a separate category of types. Diferentemente dos tipos de referência e tipos de valor, os tipos de ponteiro não herdam de object e nenhuma conversões existem entre os tipos de ponteiro e object .Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object. Em particular, boxing e unboxing (boxing e unboxing) não têm suporte para ponteiros.In particular, boxing and unboxing (Boxing and unboxing) are not supported for pointers. No entanto, as conversões são permitidas entre diferentes tipos de ponteiro e entre os tipos de ponteiro e os tipos integrais.However, conversions are permitted between different pointer types and between pointer types and the integral types. Isso é descrito em conversões de ponteiro.This is described in Pointer conversions.

Um pointer_type não pode ser usado como um argumento de tipo (tipos construídos), e a inferência de tipos (inferênciade tipos) falha em chamadas de método genérico que teriam inferido um argumento de tipo para ser um tipo de ponteiro.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.

Um pointer_type pode ser usado como o tipo de um campo volátil (campos voláteis).A pointer_type may be used as the type of a volatile field (Volatile fields).

Embora os ponteiros possam ser passados como ref out parâmetros ou, fazer isso pode causar um comportamento indefinido, já que o ponteiro pode ser bem definido para apontar para uma variável local que não existe mais quando o método chamado retorna, ou o objeto fixo ao qual ele costumava apontar, não é mais fixo.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. Por exemplo: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
        }
    }
}

Um método pode retornar um valor de algum tipo, e esse tipo pode ser um ponteiro.A method can return a value of some type, and that type can be a pointer. Por exemplo, quando um ponteiro é fornecido para uma sequência contígua de int s, a contagem de elementos dessa sequência e algum outro int valor, o método a seguir retorna o endereço desse valor nessa sequência, se ocorrer uma correspondência; caso contrário, retornará 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;
}

Em um contexto não seguro, várias construções estão disponíveis para operar em ponteiros:In an unsafe context, several constructs are available for operating on pointers:

Variáveis fixas e móveisFixed and moveable variables

O operador address-of (o operador address-of) e a fixed instrução (a instrução Fixed) dividem variáveis em duas categorias: *variáveis fixas _ e _ variáveis móveis *.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**.

As variáveis fixas residem em locais de armazenamento que não são afetados pela operação do coletor de lixo.Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. (Exemplos de variáveis fixas incluem variáveis locais, parâmetros de valor e variáveis criadas pela desreferenciação de ponteiros.) Por outro lado, as variáveis móveis residem em locais de armazenamento que estão sujeitos a realocação ou descarte pelo coletor de lixo.(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. (Exemplos de variáveis móveis incluem campos em objetos e elementos de matrizes.)(Examples of moveable variables include fields in objects and elements of arrays.)

O & operador (o operador address-of) permite que o endereço de uma variável fixa seja obtido sem restrições.The & operator (The address-of operator) permits the address of a fixed variable to be obtained without restrictions. No entanto, como uma variável móvel está sujeita a realocação ou alienação pelo coletor de lixo, o endereço de uma variável móvel só pode ser obtido usando uma fixed instrução (a instrução fixa) e esse endereço permanece válido somente pela duração dessa fixed instrução.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.

Em termos precisos, uma variável fixa é uma das seguintes:In precise terms, a fixed variable is one of the following:

  • Uma variável resultante de um Simple_name (nomes simples) que se refere a uma variável local ou a um parâmetro de valor, a menos que a variável seja capturada por uma função anônima.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.
  • Uma variável resultante de uma member_access (acesso de membro) do formulário V.I , em que V é uma variável fixa de um 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.
  • Uma variável resultante de uma pointer_indirection_expression (indireção de ponteiro) do formulário *P , uma pointer_member_access (acesso de membro de ponteiro) do formulário P->I ou uma pointer_element_access (acesso de elemento de ponteiro) do formulário 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].

Todas as outras variáveis são classificadas como variáveis móveis.All other variables are classified as moveable variables.

Observe que um campo estático é classificado como uma variável móvel.Note that a static field is classified as a moveable variable. Observe também que um ref out parâmetro ou é classificado como uma variável móvel, mesmo que o argumento fornecido para o parâmetro seja uma variável fixa.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. Por fim, observe que uma variável produzida por desreferenciar um ponteiro é sempre classificada como uma variável fixa.Finally, note that a variable produced by dereferencing a pointer is always classified as a fixed variable.

Conversões de ponteiroPointer conversions

Em um contexto sem segurança, o conjunto de conversões implícitas disponíveis (conversões implícitas) é estendido para incluir as seguintes conversões de ponteiro implícitas:In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • De qualquer pointer_type para o tipo void* .From any pointer_type to the type void*.
  • Do null literal para qualquer pointer_type.From the null literal to any pointer_type.

Além disso, em um contexto não seguro, o conjunto de conversões explícitas disponíveis (conversões explícitas) é estendido para incluir as seguintes conversões de ponteiro explícitas:Additionally, in an unsafe context, the set of available explicit conversions (Explicit conversions) is extended to include the following explicit pointer conversions:

  • De qualquer pointer_type a qualquer outro pointer_type.From any pointer_type to any other pointer_type.
  • De sbyte , byte , short , ushort , int , uint , long ou ulong para qualquer pointer_type.From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer_type.
  • De qualquer pointer_type para,,,,,, sbyte byte short ushort int uint long ou ulong .From any pointer_type to sbyte, byte, short, ushort, int, uint, long, or ulong.

Por fim, em um contexto não seguro, o conjunto de conversões implícitas padrão (conversões implícitas padrão) inclui a seguinte conversão de ponteiro:Finally, in an unsafe context, the set of standard implicit conversions (Standard implicit conversions) includes the following pointer conversion:

  • De qualquer pointer_type para o tipo void* .From any pointer_type to the type void*.

As conversões entre dois tipos de ponteiro nunca alteram o valor real do ponteiro.Conversions between two pointer types never change the actual pointer value. Em outras palavras, uma conversão de um tipo de ponteiro para outro não tem nenhum efeito sobre o endereço subjacente fornecido pelo ponteiro.In other words, a conversion from one pointer type to another has no effect on the underlying address given by the pointer.

Quando um tipo de ponteiro é convertido em outro, se o ponteiro resultante não estiver alinhado corretamente para o tipo apontado, o comportamento será indefinido se o resultado for de referência.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. Em geral, o conceito "alinhado corretamente" é transitivo: se um ponteiro para o tipo A estiver alinhado corretamente para um ponteiro para tipo B , que, por sua vez, está alinhado corretamente para que um ponteiro seja digitado C , um ponteiro para tipo A é alinhado corretamente para que um ponteiro seja digitado 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.

Considere o seguinte caso em que uma variável com um tipo é acessada por meio de um ponteiro para um tipo diferente: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 um tipo de ponteiro é convertido em um ponteiro para byte, o resultado aponta para o byte de endereçamento mais baixo da variável.When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the variable. Incrementos sucessivos do resultado, até o tamanho da variável, geram ponteiros para os bytes restantes dessa variável.Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. Por exemplo, o método a seguir exibe cada um dos oito bytes em um duplo como um valor hexadecimal: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();
        }
    }
}

É claro que a saída produzida depende da ordenação.Of course, the output produced depends on endianness.

Os mapeamentos entre ponteiros e inteiros são definidos pela implementação.Mappings between pointers and integers are implementation-defined. No entanto, em arquiteturas de CPU de 32 * e 64 bits com um espaço de endereço linear, conversões de ponteiros para ou de tipos integrais normalmente se comportam exatamente como conversões de uint ulong valores ou, respectivamente, de ou para esses tipos integral.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.

Matrizes de ponteiroPointer arrays

Em um contexto não seguro, matrizes de ponteiros podem ser construídas.In an unsafe context, arrays of pointers can be constructed. Somente algumas das conversões que se aplicam a outros tipos de matriz são permitidas em matrizes de ponteiro:Only some of the conversions that apply to other array types are allowed on pointer arrays:

  • A conversão de referência implícita (conversões de referência implícitas) de qualquer array_type para System.Array e as interfaces que ele implementa também se aplica a matrizes de ponteiro.The implicit reference conversion (Implicit reference conversions) from any array_type to System.Array and the interfaces it implements also applies to pointer arrays. No entanto, qualquer tentativa de acessar os elementos da matriz por meio System.Array do ou das interfaces que ele implementa resultará em uma exceção em tempo de execução, pois os tipos de ponteiro não são conversíveis 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.
  • As conversões de referência implícita e explícita (conversões dereferência implícita, conversões de referência explícitas) de um tipo de matriz unidimensional S[] para System.Collections.Generic.IList<T> e suas interfaces base genéricas nunca se aplicam a matrizes de ponteiro, pois tipos de ponteiro não podem ser usados como argumentos de tipo, e não há conversões de tipos de ponteiro para tipos que não sejam ponteiros.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.
  • A conversão de referência explícita (conversões de referência explícitas) System.Array e as interfaces implementadas para qualquer array_type se aplica a matrizes de ponteiro.The explicit reference conversion (Explicit reference conversions) from System.Array and the interfaces it implements to any array_type applies to pointer arrays.
  • As conversões de referência explícita (conversões de referência explícitas) de System.Collections.Generic.IList<S> e suas interfaces base para um tipo de matriz unidimensional T[] nunca se aplicam a matrizes de ponteiro, já que tipos de ponteiro não podem ser usados como argumentos de tipo, e não há conversões de tipos de ponteiro para tipos de não ponteiro.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.

Essas restrições significam que a expansão para a foreach instrução sobre matrizes descritas na instrução foreach não pode ser aplicada a matrizes de ponteiro.These restrictions mean that the expansion for the foreach statement over arrays described in The foreach statement cannot be applied to pointer arrays. Em vez disso, uma instrução foreach do formulárioInstead, a foreach statement of the form

foreach (V v in x) embedded_statement

onde o tipo de x é um tipo de matriz do formulário T[,,...,] , N é o número de dimensões menos 1 e T ou V é um tipo de ponteiro, é expandido usando loops aninhados da seguinte maneira: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
    }
}

As variáveis a , i0 , i1 ,..., iN não são visíveis ou acessíveis para x o ou o embedded_statement ou qualquer outro código-fonte do programa.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. A variável v é somente leitura na instrução incorporada.The variable v is read-only in the embedded statement. Se não houver uma conversão explícita (conversões de ponteiro) de T (o tipo de elemento) para V , um erro será produzido e nenhuma etapa adicional será executada.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 tiver o valor null , um System.NullReferenceException será lançado em tempo de execução.If x has the value null, a System.NullReferenceException is thrown at run-time.

Ponteiros em expressõesPointers in expressions

Em um contexto não seguro, uma expressão pode gerar um resultado de um tipo de ponteiro, mas fora de um contexto sem segurança, é um erro de tempo de compilação para uma expressão ser de um tipo de ponteiro.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. Em termos precisos, fora de um contexto sem segurança, um erro de tempo de compilação ocorrerá se qualquer Simple_name (nomes simples), member_access (acesso de membro), invocation_expression (expressões de invocação) ou element_access (acesso ao elemento) for de um tipo de ponteiro.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.

Em um contexto sem segurança, as produções primary_no_array_creation_expression (expressões primárias) e unary_expression (operadores unários) permitem as seguintes construções adicionais: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
    ;

Essas construções são descritas nas seções a seguir.These constructs are described in the following sections. A precedência e a Associação dos operadores inseguros é implícita pela gramática.The precedence and associativity of the unsafe operators is implied by the grammar.

Indireção de ponteiroPointer indirection

Um pointer_indirection_expression consiste em um asterisco ( * ) seguido por um unary_expression.A pointer_indirection_expression consists of an asterisk (*) followed by a unary_expression.

pointer_indirection_expression
    : '*' unary_expression
    ;

O operador unário * denota indireção de ponteiro e é usado para obter a variável para a qual um ponteiro aponta.The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. O resultado da avaliação *P , em que P é uma expressão de um tipo de ponteiro T* , é uma variável do tipo T .The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. É um erro de tempo de compilação para aplicar o operador unário * a uma expressão do tipo void* ou a uma expressão que não é de um tipo de ponteiro.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.

O efeito de aplicar o operador unário * a um null ponteiro é definido pela implementação.The effect of applying the unary * operator to a null pointer is implementation-defined. Em particular, não há nenhuma garantia de que essa operação gere um System.NullReferenceException .In particular, there is no guarantee that this operation throws a System.NullReferenceException.

Se um valor inválido tiver sido atribuído ao ponteiro, o comportamento do operador unário * será indefinido.If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. Entre os valores inválidos para desreferenciar um ponteiro pelo operador unário * estão um endereço incorretamente alinhado para o tipo apontado (consulte o exemplo em conversões de ponteiro) e o endereço de uma variável após o término de seu tempo de vida.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.

Para fins de análise de atribuição definitiva, uma variável produzida pela avaliação de uma expressão do formulário *P é considerada inicialmente atribuída (variáveis inicialmente atribuídas).For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned (Initially assigned variables).

Acesso de membro do ponteiroPointer member access

Um pointer_member_access consiste em um primary_expression, seguido por um -> token "", seguido por um identificador e um type_argument_list opcional.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
    ;

Em um membro de ponteiro acesso do formulário P->I , P deve ser uma expressão de um tipo de ponteiro diferente de void* e I deve indicar um membro acessível do tipo para o qual os P pontos.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.

Um acesso de membro de ponteiro do formulário P->I é avaliado exatamente como (*P).I .A pointer member access of the form P->I is evaluated exactly as (*P).I. Para obter uma descrição do operador de indireção de ponteiro ( * ), consulte indireção de ponteiro.For a description of the pointer indirection operator (*), see Pointer indirection. Para obter uma descrição do operador de acesso de membro ( . ), consulte acesso de membro.For a description of the member access operator (.), see Member access.

No exemploIn 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());
        }
    }
}

o -> operador é usado para acessar campos e invocar um método de uma struct por meio de um ponteiro.the -> operator is used to access fields and invoke a method of a struct through a pointer. Como a operação P->I é precisamente equivalente a (*P).I , o Main método poderia igualmente ter sido escrito: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());
        }
    }
}

Acesso de elemento do ponteiroPointer element access

Um pointer_element_access consiste em um primary_no_array_creation_expression seguido por uma expressão colocada em " [ " 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 ']'
    ;

Em um elemento de ponteiro, o acesso ao formulário P[E] P deve ser uma expressão de um tipo de ponteiro diferente de void* e E deve ser uma expressão que possa ser convertida implicitamente em int , uint , long ou 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.

Um elemento de ponteiro acesso ao formulário P[E] é avaliado exatamente como *(P + E) .A pointer element access of the form P[E] is evaluated exactly as *(P + E). Para obter uma descrição do operador de indireção de ponteiro ( * ), consulte indireção de ponteiro.For a description of the pointer indirection operator (*), see Pointer indirection. Para obter uma descrição do operador de adição de ponteiro ( + ), consulte aritmética de ponteiro.For a description of the pointer addition operator (+), see Pointer arithmetic.

No exemploIn the example

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

um acesso de elemento de ponteiro é usado para inicializar o buffer de caracteres em um for loop.a pointer element access is used to initialize the character buffer in a for loop. Como a operação P[E] é precisamente equivalente a *(P + E) , o exemplo poderia igualmente ter sido escrito: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;
        }
    }
}

O operador de acesso de elemento de ponteiro não verifica erros fora de limites e o comportamento ao acessar um elemento fora dos limites é indefinido.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. Isso é o mesmo que C e C++.This is the same as C and C++.

O operador address-ofThe address-of operator

Um addressof_expression consiste em um e comercial ( & ) seguido por um unary_expression.An addressof_expression consists of an ampersand (&) followed by a unary_expression.

addressof_expression
    : '&' unary_expression
    ;

Dada uma expressão E que é de um tipo T e é classificada como uma variável fixa (variáveis fixas e móveis), a construção &E computa o endereço da variável fornecida por 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. O tipo do resultado é T* e é classificado como um valor.The type of the result is T* and is classified as a value. Ocorrerá um erro de tempo de compilação se E não for classificado como uma variável, se E for classificado como uma variável local somente leitura, ou se for E indicado uma variável móvel.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. No último caso, uma instrução fixa (a instrução Fixed) pode ser usada para "corrigir" temporariamente a variável antes de obter seu endereço.In the last case, a fixed statement (The fixed statement) can be used to temporarily "fix" the variable before obtaining its address. Conforme indicado em acesso de membro, fora de um construtor de instância ou construtor estático para uma struct ou classe que define um readonly campo, esse campo é considerado um valor, não uma variável.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. Como tal, seu endereço não pode ser obtido.As such, its address cannot be taken. Da mesma forma, o endereço de uma constante não pode ser obtido.Similarly, the address of a constant cannot be taken.

O & operador não exige que seu argumento seja definitivamente atribuído, mas após uma & operação, a variável à qual o operador é aplicado é considerada definitivamente atribuída no caminho de execução no qual a operação ocorre.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. É de responsabilidade do programador garantir que a inicialização correta da variável realmente ocorra nessa situação.It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

No exemploIn the example

using System;

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

i é considerado definitivamente atribuído após a &i operação usada para inicializar p .i is considered definitely assigned following the &i operation used to initialize p. A atribuição a *p no efeito é inicializada i , mas a inclusão dessa inicialização é responsabilidade do programador e nenhum erro de tempo de compilação ocorrerá se a atribuição tiver sido removida.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.

As regras de atribuição definitiva para o & operador existem de modo que a inicialização redundante de variáveis locais possa ser evitada.The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. Por exemplo, muitas APIs externas usam um ponteiro para uma estrutura que é preenchida pela API.For example, many external APIs take a pointer to a structure which is filled in by the API. Chamadas para essas APIs normalmente passam o endereço de uma variável de struct local e sem a regra, a inicialização redundante da variável de struct seria necessária.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.

Incrementar e decrementar ponteirosPointer increment and decrement

Em um contexto sem segurança, os ++ -- operadores e (incremento de sufixo e diminuição de operadores e os operadores de incremento e diminuição de prefixo) podem ser aplicados a variáveis de ponteiro de todos os tipos, exceto 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*. Portanto, para cada tipo de ponteiro T* , os seguintes operadores são definidos implicitamente:Thus, for every pointer type T*, the following operators are implicitly defined:

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

Os operadores produzem os mesmos resultados de x + 1 e x - 1 , respectivamente (aritmética de ponteiro).The operators produce the same results as x + 1 and x - 1, respectively (Pointer arithmetic). Em outras palavras, para uma variável de ponteiro do tipo T* , o ++ operador adiciona sizeof(T) ao endereço contido na variável e o -- operador subtrai sizeof(T) do endereço contido na variável.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 uma operação de incremento ou diminuição de ponteiro estourar o domínio do tipo de ponteiro, o resultado será definido pela implementação, mas nenhuma exceção será produzida.If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

Aritmética do ponteiroPointer arithmetic

Em um contexto sem segurança, os + operadores and (operador de - adição e subtração) podem ser aplicados a valores de todos os tipos de ponteiro, exceto void* .In an unsafe context, the + and - operators (Addition operator and Subtraction operator) can be applied to values of all pointer types except void*. Portanto, para cada tipo de ponteiro T* , os seguintes operadores são definidos implicitamente: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);

Dada uma expressão P de um tipo de ponteiro T* e uma expressão N de tipo int , uint , long ou ulong , as expressões P + N e N + P computar o valor do ponteiro do tipo T* que resulta da adição N * sizeof(T) ao endereço fornecido por 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. Da mesma forma, a expressão P - N computa o valor de ponteiro do tipo T* que resulta da subtração N * sizeof(T) do endereço fornecido por 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.

Dadas duas expressões P e Q , de um tipo de ponteiro T* , a expressão P - Q computa a diferença entre os endereços fornecidos pelo P e Q , em seguida, divide essa diferença por 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). O tipo do resultado é sempre long .The type of the result is always long. Na verdade, P - Q é calculado como ((long)(P) - (long)(Q)) / sizeof(T) .In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

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

que produz a saída:which produces the output:

p - q = -14
q - p = 14

Se uma operação aritmética de ponteiro estoura o domínio do tipo de ponteiro, o resultado é truncado de maneira definida pela implementação, mas nenhuma exceção é produzida.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.

Comparação de ponteirosPointer comparison

Em um contexto não seguro, os == operadores,,,, != < > <= e => (operadores relacionais e de teste de tipo) podem ser aplicados aos valores de todos os tipos de ponteiro.In an unsafe context, the ==, !=, <, >, <=, and => operators (Relational and type-testing operators) can be applied to values of all pointer types. Os operadores de comparação de ponteiro são: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);

Como existe uma conversão implícita de qualquer tipo de ponteiro para o void* tipo, operandos de qualquer tipo de ponteiro podem ser comparados usando esses operadores.Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. Os operadores de comparação comparam os endereços fornecidos pelos dois operandos como se fossem inteiros sem sinal.The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

O operador sizeofThe sizeof operator

O operador sizeof retorna o número de bytes ocupados por uma variável de um determinado tipo.The sizeof operator returns the number of bytes occupied by a variable of a given type. O tipo especificado como um operando sizeof deve ser um unmanaged_type (tipos de ponteiro).The type specified as an operand to sizeof must be an unmanaged_type (Pointer types).

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

O resultado do sizeof operador é um valor do tipo int .The result of the sizeof operator is a value of type int. Para determinados tipos predefinidos, o sizeof operador produz um valor constante, conforme mostrado na tabela a seguir.For certain predefined types, the sizeof operator yields a constant value as shown in the table below.

ExpressionExpression ResultadoResult
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

Para todos os outros tipos, o resultado do sizeof operador é definido pela implementação e é classificado como um valor, não uma constante.For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

A ordem na qual os membros são empacotados em um struct não é especificada.The order in which members are packed into a struct is unspecified.

Para fins de alinhamento, pode haver um preenchimento sem nome no início de uma struct, dentro de uma struct e no final da estrutura.For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. O conteúdo dos bits usados como preenchimento é indeterminado.The contents of the bits used as padding are indeterminate.

Quando aplicado a um operando que tem o tipo struct, o resultado é o número total de bytes em uma variável desse tipo, incluindo qualquer preenchimento.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.

A instrução fixedThe fixed statement

Em um contexto sem segurança, a produção embedded_statement (instruções) permite um constructo adicional, a fixed instrução, que é usada para "corrigir" uma variável móvel, de modo que seu endereço permaneça constante durante a instrução.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
    ;

Cada fixed_pointer_declarator declara uma variável local do pointer_type especificado e inicializa essa variável local com o endereço computado pelo fixed_pointer_initializer correspondente.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. Uma variável local declarada em uma fixed instrução pode ser acessada em qualquer fixed_pointer_initializer s que ocorrem à direita da declaração dessa variável e na embedded_statement da fixed instrução.A local variable declared in a fixed statement is accessible in any fixed_pointer_initializer s occurring to the right of that variable's declaration, and in the embedded_statement of the fixed statement. Uma variável local declarada por uma fixed instrução é considerada somente leitura.A local variable declared by a fixed statement is considered read-only. Ocorrerá um erro de tempo de compilação se a instrução inserida tentar modificar essa variável local (por meio de atribuição ou os ++ -- operadores e) ou passá-la como um ref out parâmetro ou.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.

Um fixed_pointer_initializer pode ser um dos seguintes:A fixed_pointer_initializer can be one of the following:

  • O token " & " seguido por um variable_reference (regras precisas para determinar a atribuição definitiva) para uma variável móvel (variáveis fixas e móveis) de um tipo não gerenciado T , desde que o tipo T* seja implicitamente conversível para o tipo de ponteiro fornecido na fixed instrução.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. Nesse caso, o inicializador computa o endereço da variável fornecida, e a variável é garantida para permanecer em um endereço fixo para a duração da fixed instrução.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.
  • Uma expressão de um array_type com elementos de um tipo não gerenciado T , desde que o tipo T* seja implicitamente conversível para o tipo de ponteiro fornecido na fixed instrução.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. Nesse caso, o inicializador computa o endereço do primeiro elemento na matriz e toda a matriz é garantida para permanecer em um endereço fixo durante a fixed instrução.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 a expressão de matriz for nula ou se a matriz tiver zero elementos, o inicializador computará um endereço igual a zero.If the array expression is null or if the array has zero elements, the initializer computes an address equal to zero.
  • Uma expressão do tipo string , desde que o tipo char* seja conversível implicitamente no tipo de ponteiro fornecido na fixed instrução.An expression of type string, provided the type char* is implicitly convertible to the pointer type given in the fixed statement. Nesse caso, o inicializador computa o endereço do primeiro caractere na cadeia de caracteres e toda a cadeia de caracteres é garantida para permanecer em um endereço fixo para a duração da fixed instrução.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. O comportamento da fixed instrução será definido pela implementação se a expressão de cadeia de caracteres for nula.The behavior of the fixed statement is implementation-defined if the string expression is null.
  • Um Simple_name ou member_access que faz referência a um membro de buffer de tamanho fixo de uma variável móvel, desde que o tipo do membro de buffer de tamanho fixo seja implicitamente conversível para o tipo de ponteiro fornecido na fixed instrução.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. Nesse caso, o inicializador computa um ponteiro para o primeiro elemento do buffer de tamanho fixo (buffers de tamanho fixo em expressões) e é garantido que o buffer de tamanho fixo permaneça em um endereço fixo para a duração da fixed instrução.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.

Para cada endereço calculado por um fixed_pointer_initializer a fixed instrução garante que a variável referenciada pelo endereço não esteja sujeita a realocação ou descarte pelo coletor de lixo durante a fixed instrução.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. Por exemplo, se o endereço computado por um fixed_pointer_initializer referenciar um campo de um objeto ou um elemento de uma instância de matriz, a fixed instrução garante que a instância de objeto recipiente não seja realocada ou descartada durante o tempo de vida da instrução.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.

É responsabilidade do programador garantir que os ponteiros criados por fixed instruções não sobrevivem além da execução dessas instruções.It is the programmer's responsibility to ensure that pointers created by fixed statements do not survive beyond execution of those statements. Por exemplo, quando ponteiros criados por fixed instruções são passados para APIs externas, é responsabilidade do programador garantir que as APIs não mantenham memória desses ponteiros.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.

Os objetos fixos podem causar a fragmentação do heap (porque eles não podem ser movidos).Fixed objects may cause fragmentation of the heap (because they can't be moved). Por esse motivo, os objetos devem ser corrigidos somente quando absolutamente necessário e, em seguida, apenas pelo menor tempo possível.For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible.

O exemploThe 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);
        }
    }
}

demonstra vários usos da fixed instrução.demonstrates several uses of the fixed statement. A primeira instrução corrige e Obtém o endereço de um campo estático, a segunda instrução corrige e Obtém o endereço de um campo de instância, e a terceira instrução corrige e Obtém o endereço de um elemento de matriz.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. Em cada caso, seria um erro para usar o & operador regular, uma vez que as variáveis são todas classificadas como variáveis móveis.In each case it would have been an error to use the regular & operator since the variables are all classified as moveable variables.

A quarta fixed instrução no exemplo acima produz um resultado semelhante ao terceiro.The fourth fixed statement in the example above produces a similar result to the third.

Este exemplo da fixed instrução 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);
        }
    }
}

Em um contexto não seguro, os elementos de matrizes unidimensionais são armazenados em ordem de índice crescente, começando com index 0 e terminando com 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. Para matrizes multidimensionais, os elementos de matriz são armazenados de modo que os índices da dimensão mais à direita sejam aumentados primeiro, depois a dimensão à esquerda e assim por diante à esquerda.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. Dentro de uma fixed instrução que obtém um ponteiro p para uma instância de matriz a , os valores de ponteiro que variam de p para p + a.Length - 1 representar os endereços dos elementos na matriz.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. Da mesma forma, as variáveis que variam de p[0] para p[a.Length - 1] representar os elementos reais da matriz.Likewise, the variables ranging from p[0] to p[a.Length - 1] represent the actual array elements. Devido à maneira como as matrizes são armazenadas, podemos tratar uma matriz de qualquer dimensão como se ela fosse linear.Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear.

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

que produz a saída: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

No exemploIn 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);
        }
    }
}

uma fixed instrução é usada para corrigir uma matriz para que seu endereço possa ser passado para um método que usa um ponteiro.a fixed statement is used to fix an array so its address can be passed to a method that takes a pointer.

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

uma instrução Fixed é usada para corrigir um buffer de tamanho fixo de uma struct para que seu endereço possa ser usado como um ponteiro.a fixed statement is used to fix a fixed size buffer of a struct so its address can be used as a pointer.

Um char* valor produzido pela correção de uma instância de cadeia de caracteres sempre aponta para uma cadeia de caracteres terminada em nulo.A char* value produced by fixing a string instance always points to a null-terminated string. Dentro de uma instrução fixa que obtém um ponteiro p para uma instância de cadeia de caracteres s , os valores de ponteiro que variam de p para p + s.Length - 1 representar os endereços dos caracteres na cadeia de caracteres e o valor do ponteiro p + s.Length sempre aponta para um caractere nulo (o caractere com valor '\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').

Modificar objetos de tipo gerenciado por meio de ponteiros fixos pode resultar em um comportamento indefinido.Modifying objects of managed type through fixed pointers can results in undefined behavior. Por exemplo, como as cadeias de caracteres são imutáveis, é responsabilidade do programador garantir que o caractere referenciado por um ponteiro para uma cadeia de caracteres fixa não seja modificado.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.

A terminação nula automática de cadeias de caracteres é particularmente conveniente ao chamar APIs externas que esperam cadeias de caracteres "estilo C".The automatic null-termination of strings is particularly convenient when calling external APIs that expect "C-style" strings. Observe, no entanto, que uma instância de cadeia de caracteres tem permissão para conter caracteres nulos.Note, however, that a string instance is permitted to contain null characters. Se esses caracteres nulos estiverem presentes, a cadeia de caracteres aparecerá truncada quando tratada como terminada em nulo char* .If such null characters are present, the string will appear truncated when treated as a null-terminated char*.

Buffers de tamanho fixoFixed size buffers

Buffers de tamanho fixo são usados para declarar matrizes em linha "C style" como membros de structs e são principalmente úteis para a interface com APIs não gerenciadas.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.

Declarações de buffer de tamanho fixoFixed size buffer declarations

Um buffer de tamanho fixo é um membro que representa o armazenamento de um buffer de comprimento fixo de variáveis de um determinado tipo.A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. Uma declaração de buffer de tamanho fixo apresenta um ou mais buffers de tamanho fixo de um determinado tipo de elemento.A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. Buffers de tamanho fixo só são permitidos em declarações struct e só podem ocorrer em contextos não seguros (contextos não seguros).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 ']'
    ;

Uma declaração de buffer de tamanho fixo pode incluir um conjunto de atributos (atributos), um new modificador (modificadores), uma combinação válida dos quatro modificadores de acesso (parâmetros de tipo e restrições) e um unsafe modificador (contextos não seguros).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). Os atributos e os modificadores se aplicam a todos os membros declarados pela declaração de buffer de tamanho fixo.The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. É um erro para que o mesmo modificador apareça várias vezes em uma declaração de buffer de tamanho fixo.It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.

Uma declaração de buffer de tamanho fixo não tem permissão para incluir o static modificador.A fixed size buffer declaration is not permitted to include the static modifier.

O tipo de elemento de buffer de uma declaração de buffer de tamanho fixo especifica o tipo de elemento dos buffers introduzidos pela declaração.The buffer element type of a fixed size buffer declaration specifies the element type of the buffer(s) introduced by the declaration. O tipo de elemento de buffer deve ser um dos tipos predefinidos,,,,,,,, sbyte byte ,, short ushort int uint long ulong char float double ou 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.

O tipo de elemento buffer é seguido por uma lista de declaradores de buffer de tamanho fixo, e cada um deles introduz um novo membro.The buffer element type is followed by a list of fixed size buffer declarators, each of which introduces a new member. Um Declarador de buffer de tamanho fixo consiste em um identificador que nomeia o membro, seguido por uma expressão constante incluída em [ ] tokens e.A fixed size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in [ and ] tokens. A expressão constante denota o número de elementos no membro introduzidos pelo Declarador de buffer de tamanho fixo.The constant expression denotes the number of elements in the member introduced by that fixed size buffer declarator. O tipo da expressão constante deve ser implicitamente conversível para int o tipo, e o valor deve ser um inteiro positivo diferente de zero.The type of the constant expression must be implicitly convertible to type int, and the value must be a non-zero positive integer.

Os elementos de um buffer de tamanho fixo têm garantia de serem dispostos em sequência na memória.The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.

Uma declaração de buffer de tamanho fixo que declara vários buffers de tamanho fixo é equivalente a várias declarações de uma única declaração de buffer de tamanho fixo com os mesmos atributos e tipos de 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. Por exemplo,For example

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

é equivalente ais equivalent to

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

Buffers de tamanho fixo em expressõesFixed size buffers in expressions

A pesquisa de Membros (operadores) de um membro de buffer de tamanho fixo prossegue exatamente como a pesquisa de membros de um campo.Member lookup (Operators) of a fixed size buffer member proceeds exactly like member lookup of a field.

Um buffer de tamanho fixo pode ser referenciado em uma expressão usando uma Simple_name (inferência de tipos) ou uma member_access (verificação de tempo de compilação de resolução dinâmica de sobrecarga).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 um membro de buffer de tamanho fixo é referenciado como um nome simples, o efeito é o mesmo que um membro de acesso do formulário this.I , em que I é o membro do buffer de tamanho fixo.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.

Em um acesso de membro do formulário E.I , se E for de um tipo struct e uma pesquisa de membro de I nesse tipo struct identificar um membro de tamanho fixo, o E.I será avaliado como o seguinte: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 a expressão E.I não ocorrer em um contexto sem segurança, ocorrerá um erro em tempo de compilação.If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
  • Se E for classificado como um valor, ocorrerá um erro em tempo de compilação.If E is classified as a value, a compile-time error occurs.
  • Caso contrário, se E for uma variável móvel (variáveis fixas e móveis) e a expressão E.I não for uma fixed_pointer_initializer (a instrução Fixed), ocorrerá um erro em tempo de compilação.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.
  • Caso contrário, E o faz referência a uma variável fixa e o resultado da expressão é um ponteiro para o primeiro elemento do membro de buffer de tamanho fixo I em 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. O resultado é do tipo S* , em que S é o tipo de elemento de I e é classificado como um valor.The result is of type S*, where S is the element type of I, and is classified as a value.

Os elementos subsequentes do buffer de tamanho fixo podem ser acessados usando operações de ponteiro do primeiro elemento.The subsequent elements of the fixed size buffer can be accessed using pointer operations from the first element. Ao contrário do acesso a matrizes, o acesso aos elementos de um buffer de tamanho fixo é uma operação não segura e não é verificado.Unlike access to arrays, access to the elements of a fixed size buffer is an unsafe operation and is not range checked.

O exemplo a seguir declara e usa uma struct com um membro de buffer de tamanho fixo.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);
    }
}

Verificação de atribuição definitivaDefinite assignment checking

Os buffers de tamanho fixo não estão sujeitos à verificação de atribuição definitiva (atribuição definitiva) e os membros do buffer de tamanho fixo são ignorados para fins de verificação de atribuição definitiva de variáveis de 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 o que contém a variável de struct mais externa de um membro de buffer de tamanho fixo é uma variável estática, uma variável de instância de uma instância de classe ou um elemento de matriz, os elementos do buffer de tamanho fixo são inicializados automaticamente para seus valores padrão (valores padrão).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). Em todos os outros casos, o conteúdo inicial de um buffer de tamanho fixo é indefinido.In all other cases, the initial content of a fixed size buffer is undefined.

Alocação da pilhaStack allocation

Em um contexto não seguro, uma declaração de variável local (declarações de variável local) pode incluir um inicializador de alocação de pilha que aloca memória da pilha de chamadas.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 ']'
    ;

O unmanaged_type indica o tipo dos itens que serão armazenados no local alocado recentemente e a expressão indica o número desses itens.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. Em conjunto, eles especificam o tamanho de alocação necessário.Taken together, these specify the required allocation size. Como o tamanho de uma alocação de pilha não pode ser negativo, é um erro de tempo de compilação para especificar o número de itens como um constant_expression que é avaliado como um valor 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.

Um inicializador de alocação de pilha do formulário stackalloc T[E] requer T que seja um tipo não gerenciado (tipos de ponteiro) e E seja uma expressão do 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. A construção aloca E * sizeof(T) bytes da pilha de chamadas e retorna um ponteiro, do tipo T* , para o bloco recentemente alocado.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 for um valor negativo, o comportamento será indefinido.If E is a negative value, then the behavior is undefined. Se E for zero, nenhuma alocação será feita e o ponteiro retornado será definido como implementação.If E is zero, then no allocation is made, and the pointer returned is implementation-defined. Se não houver memória suficiente disponível para alocar um bloco de determinado tamanho, um System.StackOverflowException será lançado.If there is not enough memory available to allocate a block of the given size, a System.StackOverflowException is thrown.

O conteúdo da memória recém-alocada é indefinido.The content of the newly allocated memory is undefined.

Inicializadores de alocação de pilha não são permitidos em catch finally blocos ou (a instrução try).Stack allocation initializers are not permitted in catch or finally blocks (The try statement).

Não é possível liberar explicitamente a memória alocada usando stackalloc .There is no way to explicitly free memory allocated using stackalloc. Todos os blocos de memória alocada na pilha criados durante a execução de um membro de função são automaticamente descartados quando esse membro de função retorna.All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. Isso corresponde à alloca função, uma extensão normalmente encontrada em implementações de C e C++.This corresponds to the alloca function, an extension commonly found in C and C++ implementations.

No exemploIn 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));
    }
}

um stackalloc inicializador é usado no IntToString método para alocar um buffer de 16 caracteres na pilha.a stackalloc initializer is used in the IntToString method to allocate a buffer of 16 characters on the stack. O buffer é descartado automaticamente quando o método retorna.The buffer is automatically discarded when the method returns.

Alocação de memória dinâmicaDynamic memory allocation

Exceto para o stackalloc operador, o C# não fornece construções predefinidas para gerenciar memória não coletada pelo lixo.Except for the stackalloc operator, C# provides no predefined constructs for managing non-garbage collected memory. Esses serviços normalmente são fornecidos pelo suporte a bibliotecas de classes ou importados diretamente do sistema operacional subjacente.Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. Por exemplo, a Memory classe a seguir ilustra como as funções de heap de um sistema operacional subjacente podem ser acessadas do C#: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);
}

Um exemplo que usa a Memory classe é fornecido abaixo: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);
        }
    }
}

O exemplo aloca 256 bytes de memória por meio do Memory.Alloc e inicializa o bloco de memória com valores que aumentam de 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. Em seguida, ele aloca uma matriz de bytes do elemento 256 e usa Memory.Copy para copiar o conteúdo do bloco de memória para a matriz de bytes.It then allocates a 256 element byte array and uses Memory.Copy to copy the contents of the memory block into the byte array. Por fim, o bloco de memória é liberado usando Memory.Free e o conteúdo da matriz de bytes é apresentado no console.Finally, the memory block is freed using Memory.Free and the contents of the byte array are output on the console.