Operadores relacionados a ponteiro – pegar o endereço de variáveis, desreferenciar locais de armazenamento e acessar locais de memória

Os operadores de ponteiro permitem que você pegue o endereço de uma variável (&), desreferencie um ponteiro (*), compare valores de ponteiro e adicione ou subtraia ponteiros e inteiros.

Você pode usar os seguintes operadores para trabalhar com ponteiros:

Para obter informações sobre tipos de ponteiros, veja Tipos de ponteiro.

Observação

Qualquer operação com ponteiros exige um contexto unsafe. O código que contém blocos não seguros deve ser compilado com a opção do compilador AllowUnsafeBlocks.

Operador address-of &

O operador unário & retorna o endereço de seu operando:

unsafe
{
    int number = 27;
    int* pointerToNumber = &number;

    Console.WriteLine($"Value of the variable: {number}");
    Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4

O operando do operador & deve ser uma variável fixa. Variáveis fixas são variáveis que residem em locais de armazenamento não afetados pela operação do coletor de lixo. No exemplo anterior, a variável local number é uma variável fixa, pois reside na pilha. Variáveis que residem em locais de armazenamento que podem ser afetados pelo coletor de lixo (por exemplo, realocado) são chamadas de variáveis móveis. Campos de objeto e elementos de matriz são exemplos de variáveis móveis. Você poderá obter o endereço de uma variável móvel se a "firmar" ou "fixar" com uma instrução fixed. O endereço obtido é válido somente dentro do bloco de uma instrução fixed. O exemplo a seguir mostra como usar uma instrução fixed e o operador &:

unsafe
{
    byte[] bytes = { 1, 2, 3 };
    fixed (byte* pointerToFirst = &bytes[0])
    {
        // The address stored in pointerToFirst
        // is valid only inside this fixed statement block.
    }
}

Não é possível obter o endereço de uma constante nem de um valor.

Para obter mais informações sobre variáveis fixas e móveis, veja a seção Variáveis fixas e móveis da Especificação de linguagem C#.

O operador binário & computa o AND lógico de seus operandos Boolianos ou o AND lógico bit a bit de seus operandos integrais.

Operador de indireção de ponteiro*

O operador unário de indireção de ponteiro * obtém a variável para a qual o operando aponta. Também é conhecido como o operador de desreferenciar. O operando do operador * deve ser de um tipo de ponteiro.

unsafe
{
    char letter = 'A';
    char* pointerToLetter = &letter;
    Console.WriteLine($"Value of the `letter` variable: {letter}");
    Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");

    *pointerToLetter = 'Z';
    Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z

Não é possível aplicar o operador * a uma expressão do tipo void*.

O operador binário * computa o produto de seus operandos numéricos.

Operador de acesso a membro do ponteiro ->

O operador -> combina indireção do ponteiro e acesso de membro. Ou seja, se x for um ponteiro do tipo T* e y for um membro acessível de T, uma expressão do formulário

x->y

é equivalente a

(*x).y

O exemplo a seguir demonstra o uso do operador ->:

public struct Coords
{
    public int X;
    public int Y;
    public override string ToString() => $"({X}, {Y})";
}

public class PointerMemberAccessExample
{
    public static unsafe void Main()
    {
        Coords coords;
        Coords* p = &coords;
        p->X = 3;
        p->Y = 4;
        Console.WriteLine(p->ToString());  // output: (3, 4)
    }
}

Não é possível aplicar o operador -> a uma expressão do tipo void*.

Operador de acesso a elemento do ponteiro []

Para uma expressão p de um tipo de ponteiro, um acesso de elemento de ponteiro da forma p[n] é avaliado como *(p + n), em que n deve ser do tipo implicitamente conversível em int, uint, long ou ulong. Para obter informações sobre o comportamento do operador + com ponteiros, veja a seção Adição ou subtração de um valor integral para ou de um ponteiro.

O exemplo a seguir demonstra como acessar elementos da matriz com um ponteiro e o operador []:

unsafe
{
    char* pointerToChars = stackalloc char[123];

    for (int i = 65; i < 123; i++)
    {
        pointerToChars[i] = (char)i;
    }

    Console.Write("Uppercase letters: ");
    for (int i = 65; i < 91; i++)
    {
        Console.Write(pointerToChars[i]);
    }
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ

No exemplo anterior, uma expressão stackalloc aloca um bloco de memória na pilha.

Observação

O operador de acesso de elemento de ponteiro não verifica se há erros fora dos limites.

Não é possível usar [] para acesso de elemento de ponteiro com uma expressão do tipo void*.

Você também pode usar o operador [] para acesso de indexador ou elemento de matriz.

Operadores aritméticos de ponteiro

Você pode executar as seguintes operações aritméticas com ponteiros:

  • Adicionar ou subtrair um valor integral de ou para um ponteiro
  • Subtrair dois ponteiros
  • Incrementar ou decrementar um ponteiro

Você não pode executar essas operações com ponteiros do tipo void*.

Para obter informações sobre operações aritméticas com suporte com tipos numéricos, veja Operadores aritméticos.

Adição ou subtração de um valor integral a ou de um ponteiro

Para um ponteiro p do tipo T* e uma expressão n de um tipo implicitamente conversível em int, uint, long ou ulong, adição e subtração são definidas da seguinte maneira:

  • As expressões p + n e n + p produzem um ponteiro do tipo T* que resulta da adição de n * sizeof(T) ao endereço fornecido pelo p.
  • A expressão p - n produz um ponteiro do tipo T* que resulta da subtração de n * sizeof(T) ao endereço fornecido pelo p.

O operador sizeof obtém o tamanho de um tipo em bytes.

O exemplo a seguir demonstra o uso do operador + com um ponteiro:

unsafe
{
    const int Count = 3;
    int[] numbers = new int[Count] { 10, 20, 30 };
    fixed (int* pointerToFirst = &numbers[0])
    {
        int* pointerToLast = pointerToFirst + (Count - 1);

        Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
        Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
    }
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144

Subtração de ponteiro

Para dois ponteiros p1 e p2 do tipo T*, a expressão p1 - p2 produz a diferença entre os endereços dados por p1 e p2 divididos por sizeof(T). O tipo de resultado é long. Ou seja, p1 - p2 é computado como ((long)(p1) - (long)(p2)) / sizeof(T).

O exemplo a seguir demonstra a subtração de ponteiro:

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
    int* p1 = &numbers[1];
    int* p2 = &numbers[5];
    Console.WriteLine(p2 - p1);  // output: 4
}

Incrementar e decrementar ponteiros

O operador de incremento ++adiciona 1 ao operando do ponteiro. O operador de decremento --subtrai 1 do operando do ponteiro.

Os dois operadores têm suporte em duas formas: sufixo (p++ e p--) e prefixo (++p e --p). O resultado de p++ e p-- é o valor de pantes da operação. O resultado de ++p e --p é o valor de pdepois da operação.

O exemplo a seguir demonstra o comportamento dos operadores de incremento de sufixo e prefixo:

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2 };
    int* p1 = &numbers[0];
    int* p2 = p1;
    Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
    Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
    Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
    Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516

Operadores de comparação de ponteiro

Você pode usar os operadores ==, !=, <, >, <= e >= para comparar os operandos de qualquer tipo de ponteiro, incluindo void*. Esses operadores comparam os endereços fornecidos pelos dois operandos como se fossem inteiros sem sinal.

Para obter informações sobre o comportamento desses operadores para operandos de outros tipos, veja os artigos Operadores de igualdade e Operadores de comparação.

Precedência do operador

A lista a seguir ordena operadores relacionados a ponteiro começando da precedência mais alta até a mais baixa:

  • Operadores de incremento x++ e decremento x-- sufixados e os operadores -> e []
  • Operadores de incremento ++x e decremento --x prefixados e os operadores & e *
  • Operadores de adição + e -
  • Operadores de comparação <, >, <= e >=
  • Operadores de igualdade == e !=

Use parênteses, (), para alterar a ordem de avaliação imposta pela precedência do operador.

Para obter a lista completa de operadores C# ordenados por nível de precedência, consulte a seção Precedência de operador do artigo Operadores C# .

Capacidade de sobrecarga do operador

Um tipo definido pelo usuário não pode sobrecarregar os operadores relacionados a ponteiro &, *, -> e [].

Especificação da linguagem C#

Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:

Confira também