Partilhar via


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.

A referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em versões preliminares públicas para a próxima versão da linguagem.

A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.

Sugestão

Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.

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 . Deve compilar o código que contém blocos inseguros com a opção do 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 o coletor de lixo não afeta. 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 o coletor de lixo pode afetar (por exemplo, realocar) são chamadas variáveis móveis . Campos de objeto e elementos de matriz são exemplos de variáveis móveis. Pode obter o endereço de uma variável móvel se a "fixar" ou "fixar" usando 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 * de indireção do ponteiro unário acede à variável para a qual o 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 membro do ponteiro ->

O -> operador combina indireção de ponteiro e acesso de membro. Se x é um ponteiro do tipo T* e y é um membro acessível do tipo T, uma expressão da forma

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 podes usar o -> operador numa expressão do tipo void*.

Operador de acesso ao elemento do ponteiro []

Para uma expressão p de tipo ponteiro, um acesso a elementos de ponteiro da forma p[n] é avaliado como *(p + n). O valor n deve ser de um tipo implicitamente convertí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 seguinte demonstra como aceder a elementos do array usando 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 [] 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 informações sobre operações aritméticas suportadas usando tipos numéricos, veja Operadores aritméticos.

Adicionar ou subtrair um valor integral de ou para um ponteiro

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

  • Ambos p + n e n + p dão-te um indicador do tipo T*. Obtém-se esta dica ao adicionar n * sizeof(T) ao endereço para onde p aponta.
  • A p - n expressão dá-lhe um ponteiro do tipo T*. Obtém-se este indicador subtraindo n * sizeof(T) do endereço para onde p aponta.

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

O exemplo seguinte mostra como usar o + 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 dá-lhe a diferença entre os endereços que p1 e p2 apontam para, dividida por sizeof(T). O resultado é do tipo long. Por outras palavras, p1 - p2 é calculado como ((long)(p1) - (long)(p2)) / sizeof(T).

O exemplo seguinte mostra a subtração de ponteiros:

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 suportam duas formas: postfixo (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*. Estes operadores comparam os endereços dados pelos dois operandos como se fossem inteiros sem sinal.

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

Não podes sobrecarregar os operadores &relacionados com o ponteiro , *, ->, e [] num tipo definido pelo utilizador.

Especificação da linguagem C#

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

Consulte também