Operadores relacionados a ponteiro - pegue o endereço de variáveis, cancele a referência de locais de armazenamento e acesse locais de memória

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

Use os seguintes operadores para trabalhar com ponteiros:

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

Nota

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

Endereço do operador &

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 que não são afetados pela operação do coletor de lixo. No exemplo anterior, a variável number local é uma variável fixa, porque reside na pilha. As variáveis que residem em locais de armazenamento que podem ser afetadas 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ê pode obter o endereço de uma variável móvel se você "corrigir", ou "pinar", com uma fixed instrução. O endereço obtido é válido apenas dentro do bloco de uma fixed declaração. O exemplo a seguir mostra como usar uma fixed instrução 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 ou de um valor.

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

O operador binário & calcula o AND lógico de seus operandos booleanos ou o bit lógico AND de seus operandos integrais.

Operador de indireção de ponteiro *

O operador * unary pointer indirection obtém a variável para a qual seu operando aponta. Também é conhecido como operador de desreferência. O operando do * operador deve ser do tipo 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 * calcula o produto de seus operandos numéricos.

Operador de acesso de membro ponteiro ->

O -> operador combina indireção de ponteiro e acesso de membro. Ou seja, se x é um ponteiro do tipo T* e y é um membro acessível do tipo 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 ao elemento de ponteiro []

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

O exemplo a seguir demonstra como acessar elementos de 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 stackalloc expressão aloca um bloco de memória na pilha.

Nota

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

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

Você também pode usar o operador para acesso a [] elementos de matriz ou indexadores.

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 diminuir um ponteiro

Não é possível executar essas operações com ponteiros do tipo void*.

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

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

Para um ponteiro p de tipo T* e uma expressão n de um tipo implicitamente convertível em int, uint, , longou ulong, adição e subtração são definidas da seguinte forma:

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

O sizeof operador 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 do 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 do 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
}

Incremento e decréscimo do ponteiro

O ++ operador de incremento adiciona 1 ao seu operando de ponteiro. O -- operador de decréscimo subtrai 1 de seu operando de ponteiro.

Ambos os operadores são suportados em duas formas: postfix (p++ e p--) e prefixo (++p e --p). O resultado de p++ e p-- é o valor de antes da poperação. O resultado de ++p e --p é o valor de papós a operação.

O exemplo a seguir demonstra o comportamento dos operadores de incremento postfix 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 ponteiros

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

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

Precedência dos operadores

A lista a seguir ordena os operadores relacionados ao ponteiro a partir da maior precedência para a mais baixa:

  • Operadores de incremento e decréscimo x++x-- pós-fixo e os -> operadores e []
  • Operadores de incremento ++x e decréscimo --x de prefixo e os & operadores e *
  • + Aditivos e - operadores
  • Comparação <, >, <=, e >= operadores
  • Igualdade == e != operadores

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 do operador do artigo Operadores C#.

Capacidade de sobrecarga do operador

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

Especificação da linguagem C#

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

Consulte também