0Pricing
C# Academy · Aula

Código não seguro, ponteiros e buffers fixos

Use a palavra-chave unsafe, trabalhe com ponteiros, fixe a memória gerenciada com fixed e acesse buffers de tamanho fixo em estruturas.

Código não seguro, ponteiros e buffers fixos é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

O que é código unsafe?

A palavra-chave unsafe do C# permite a manipulação direta da memória: ponteiros brutos, aritmética de ponteiros e buffers de tamanho fixo. Ela contorna as garantias de segurança do coletor de lixo. Habilite-a com <AllowUnsafeBlocks>true</AllowUnsafeBlocks> no arquivo do projeto.

// Must add to .csproj:
// <AllowUnsafeBlocks>true</AllowUnsafeBlocks>

// Unsafe blocks can appear inside methods:
unsafe
{
    int x = 42;
    int* ptr = &x;          // get address of x
    Console.WriteLine(*ptr); // dereference — prints 42
    *ptr = 100;
    Console.WriteLine(x);    // 100 — x was mutated via pointer
}

Tipos de ponteiro em C#

A sintaxe de ponteiros é semelhante à de C/C++: T* é um ponteiro para T. É possível declarar ponteiros para tipos de valor não gerenciados (int, double e estruturas sem campos de referência). Ponteiros para tipos gerenciados não são permitidos.

unsafe
{
    int    i = 10;
    double d = 3.14;

    int*    ip = &i;
    double* dp = &d;

    // Dereference with *
    Console.WriteLine(*ip); // 10

    // Pointer arithmetic — move to next int in memory
    int[] arr = { 1, 2, 3 };
    fixed (int* p = arr)
    {
        Console.WriteLine(*(p + 0)); // 1
        Console.WriteLine(*(p + 1)); // 2
        Console.WriteLine(*(p + 2)); // 3
    }
}

A instrução fixed

Objetos gerenciados podem ser realocados pelo coletor de lixo. Para obter o endereço de um objeto gerenciado, você deve fixá-lo com fixed. O coletor de lixo não moverá o objeto durante a execução do bloco fixed.

byte[] buffer = new byte[256];

unsafe
{
    fixed (byte* pBuf = buffer)
    {
        // pBuf is valid only inside this block
        // GC won't relocate 'buffer' here
        for (int i = 0; i < buffer.Length; i++)
            pBuf[i] = (byte)i;
    }
    // After 'fixed', GC can move 'buffer' again
}

Console.WriteLine(buffer[5]); // 5

Aritmética de ponteiros

É possível adicionar ou subtrair inteiros de ponteiros. Adicionar 1 a um int* o avança em 4 bytes (tamanho de int). Essa é a base de operações rápidas com grandes blocos de memória.

unsafe
{
    int[] data = { 10, 20, 30, 40, 50 };
    fixed (int* start = data)
    {
        int* p = start;
        long sum = 0;
        for (int i = 0; i < data.Length; i++)
        {
            sum += *p;
            p++;    // advance by sizeof(int) = 4 bytes
        }
        Console.WriteLine(sum); // 150

        // Or with index syntax:
        Console.WriteLine(start[2]); // 30
    }
}

stackalloc: alocação na pilha

stackalloc aloca um bloco de memória na pilha, não no heap. Não há pressão sobre o coletor de lixo nem necessidade de fixação. A alocação é limitada ao quadro de pilha do método atual — a memória é liberada automaticamente quando o método retorna.

// Stack-allocated buffer — zero heap allocation
Span<int> buffer = stackalloc int[128]; // safe Span wrapper
buffer.Fill(0);
buffer[0] = 42;
Console.WriteLine(buffer[0]); // 42

// Or raw pointer form (requires unsafe):
unsafe
{
    int* raw = stackalloc int[128];
    raw[0] = 99;
    Console.WriteLine(raw[0]); // 99
}
// Stack frame popped — memory gone

Buffers de tamanho fixo em estruturas

Um buffer de tamanho fixo incorpora um vetor de comprimento fixo diretamente em uma estrutura — sem alocação no heap e sem ponteiro adicional. Use o modificador fixed dentro de uma estrutura declarada em um contexto unsafe.

unsafe struct NetworkPacket
{
    public int  Length;
    public byte Command;
    public fixed byte Payload[256]; // 256 bytes inline in the struct
}

unsafe
{
    NetworkPacket pkt;
    pkt.Length  = 10;
    pkt.Command = 0x01;

    // Fill payload inline — no heap allocation
    for (int i = 0; i < 10; i++)
        pkt.Payload[i] = (byte)i;

    Console.WriteLine(pkt.Payload[5]); // 5
}
// sizeof(NetworkPacket) = 4 + 1 + 256 = ~261 bytes on stack

void* e conversão de tipos

Um void* é um ponteiro sem tipo — útil para operações genéricas de memória. É necessário convertê-lo em um ponteiro tipado antes de desreferenciá-lo. O operador sizeof(T) funciona com tipos não gerenciados em um contexto unsafe.

unsafe
{
    int value = 42;
    void* vp = &value;      // typeless pointer
    int*  ip = (int*)vp;    // cast back to int*
    Console.WriteLine(*ip); // 42

    // sizeof works on unmanaged types
    Console.WriteLine(sizeof(int));    // 4
    Console.WriteLine(sizeof(double)); // 8
    Console.WriteLine(sizeof(long));   // 8
}

As classes MemoryMarshal e Unsafe

System.Runtime.CompilerServices.Unsafe e System.Runtime.InteropServices.MemoryMarshal fornecem alternativas seguras, sem palavra-chave, para muitas operações de baixo nível, compatíveis com Span e utilizáveis sem a palavra-chave unsafe.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// Reinterpret a Span<byte> as Span<int> — no copy, no unsafe keyword
byte[] raw = new byte[16];
Span<int> ints = MemoryMarshal.Cast<byte, int>(raw);
ints[0] = 12345678;
Console.WriteLine(ints[0]); // 12345678

// Unsafe.As<,> — reinterpret reference type (advanced)
ref int first = ref MemoryMarshal.GetReference(ints);
Unsafe.Add(ref first, 1) = 99999;
Console.WriteLine(ints[1]); // 99999

Quando usar — e não usar — unsafe

Use código unsafe somente quando houver uma necessidade clara de desempenho e nenhuma alternativa segura. Mantenha a superfície unsafe mínima — isole-a em métodos auxiliares privados ou tipos dedicados. Escreva testes exaustivos e documente as suposições.

// GOOD: narrow unsafe scope
public static unsafe int SumBytes(ReadOnlySpan<byte> data)
{
    int total = 0;
    fixed (byte* p = data)
    {
        byte* end = p + data.Length;
        for (byte* cur = p; cur < end; cur++)
            total += *cur;
    }
    return total;
}

// BETTER for modern code: use SIMD via Vector<T> or hardware intrinsics
// which stay in managed code but still achieve native-level perf

Exemplo real: analisando um protocolo binário

Ponteiros unsafe e buffers de tamanho fixo são especialmente úteis ao analisar protocolos binários nos quais o layout da estrutura precisa corresponder exatamente ao formato de transmissão. A estrutura pode ser convertida directly a partir de um buffer de bytes, sem cópias.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct MessageHeader
{
    public uint  Magic;        // 4 bytes
    public ushort Version;    // 2 bytes
    public ushort PayloadLen; // 2 bytes
    public fixed byte Id[16]; // 16 bytes GUID inline
}

unsafe ReadOnlySpan<byte> SerializeHeader(MessageHeader hdr)
{
    // Cast struct directly to bytes — zero copy
    return new ReadOnlySpan<byte>(&hdr, sizeof(MessageHeader));
}

unsafe MessageHeader ParseHeader(ReadOnlySpan<byte> buf)
{
    fixed (byte* p = buf)
        return *(MessageHeader*)p; // reinterpret cast
}

Verificação rápida

Por que a instrução fixed é necessária ao obter o endereço de um objeto gerenciado?

Recapitulação: código unsafe, ponteiros e buffers fixos

Principais conclusões:

  • A palavra-chave unsafe permite usar ponteiros brutos — exige AllowUnsafeBlocks no projeto
  • A instrução fixed fixa objetos gerenciados para que o coletor de lixo não os realoque
  • A aritmética de ponteiros avança pelo tamanho do elemento, não pelo número de bytes
  • stackalloc aloca na pilha — sem pressão sobre o coletor de lixo, liberando a memória ao sair do método
  • Buffers de tamanho fixo (fixed byte Payload[N]) incorporam vetores diretamente nas estruturas
  • Prefira as classes Span<T>, MemoryMarshal e Unsafe na maioria dos cenários

Perguntas Frequentes

A aula “Código não seguro, ponteiros e buffers fixos” é grátis?

Sim — o texto completo de “Código não seguro, ponteiros e buffers fixos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Código não seguro, ponteiros e buffers fixos”?

Use a palavra-chave unsafe, trabalhe com ponteiros, fixe a memória gerenciada com fixed e acesse buffers de tamanho fixo em estruturas. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Código não seguro, ponteiros e buffers fixos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos de P/Invoke
  2. LibraryImport e P/Invoke gerado pela fonte
  3. Código não seguro, ponteiros e buffers fixos
  4. Interoperação com COM e wrappers chamáveis em tempo de execução
← Voltar para C# Academy