0Pricing
C# Academy · Lección

Código no seguro, punteros y búferes fijos

Utilice la palabra clave unsafe, trabaje con punteros, fije la memoria administrada con fixed y acceda a búferes de tamaño fijo en structs.

Código no seguro, punteros y búferes fijos es una lección gratuita de C# Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C# Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C# Academy incluye 4 lecciones en total.

¿Qué es el código unsafe?

La palabra clave unsafe de C# permite manipular directamente la memoria: punteros sin procesar, aritmética de punteros y búferes de tamaño fijo. Omite las garantías de seguridad del GC. Habilítelo con <AllowUnsafeBlocks>true</AllowUnsafeBlocks> en el archivo del proyecto.

// 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 puntero en C#

La sintaxis de los punteros es similar a la de C/C++: T* es un puntero a T. Puede declarar punteros a tipos de valor no administrados (int, double y estructuras sin campos de referencia). No se permiten punteros a tipos administrados.

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
    }
}

La instrucción fixed

El GC puede reubicar los objetos administrados. Para obtener la dirección de un objeto administrado, debe anclarlo con fixed. El GC no moverá el objeto durante la ejecución del bloque 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 punteros

Puede sumar o restar enteros a los punteros. Sumar 1 a un int* lo avanza 4 bytes (sizeof int). Esta es la base de las operaciones rápidas de memoria masiva.

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: asignación en la pila

stackalloc asigna un bloque de memoria en la pila, no en el montón. No genera presión sobre el GC ni requiere anclaje. Está limitado al marco de pila del método actual; la memoria se libera automáticamente cuando el 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

Búferes de tamaño fijo en estructuras

Un búfer de tamaño fijo integra un array de longitud fija directamente en una estructura: no requiere asignación en el montón ni un puntero adicional. Use el modificador fixed dentro de una estructura declarada en un 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* y conversiones de tipo

Un void* es un puntero sin tipo, útil para operaciones de memoria genéricas. Debe convertirlo a un puntero tipado antes de desreferenciarlo. El operador sizeof(T) funciona con tipos no administrados en un 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
}

MemoryMarshal y la clase Unsafe

System.Runtime.CompilerServices.Unsafe y System.Runtime.InteropServices.MemoryMarshal proporcionan alternativas seguras (sin usar palabras clave) para muchas operaciones de bajo nivel, compatibles con Span y utilizables sin la palabra clave 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

Cuándo usar unsafe y cuándo no

Use código unsafe solo cuando exista una necesidad clara de rendimiento y no haya una alternativa segura. Mantenga al mínimo la superficie unsafe: aíslela en métodos auxiliares privados o tipos específicos. Escriba pruebas exhaustivas y documente las suposiciones.

// 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

Caso real: análisis de un protocolo binario

Los punteros unsafe y los búferes de tamaño fijo resultan especialmente útiles al analizar protocolos binarios cuyo diseño de estructura debe coincidir exactamente con un formato de transmisión. La estructura puede convertirse directamente desde un búfer de bytes sin realizar copias.

[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
}

Comprobación rápida

¿Por qué se necesita la instrucción fixed al obtener la dirección de un objeto administrado?

Resumen: código Unsafe, punteros y búferes fijos

Aspectos clave:

  • La palabra clave unsafe permite usar punteros sin procesar; requiere AllowUnsafeBlocks en el proyecto
  • La instrucción fixed ancla los objetos administrados para que el GC no los reubique
  • La aritmética de punteros avanza según el tamaño del elemento, no según los bytes
  • stackalloc asigna memoria en la pila: no genera presión sobre el GC y se libera al salir del método
  • Los búferes de tamaño fijo (fixed byte Payload[N]) integran arrays directamente en las estructuras
  • Prefiera Span<T>, MemoryMarshal y la clase Unsafe en la mayoría de los casos

Preguntas frecuentes

¿La lección «Código no seguro, punteros y búferes fijos» es gratis?

Sí — el texto completo de «Código no seguro, punteros y búferes fijos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C# Academy, actualiza a CoddyKit PRO. El curso de C# Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Código no seguro, punteros y búferes fijos»?

Utilice la palabra clave unsafe, trabaje con punteros, fije la memoria administrada con fixed y acceda a búferes de tamaño fijo en structs. Practicas C# Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar C# Academy?

No se requiere experiencia previa. C# Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Código no seguro, punteros y búferes fijos»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de C# Academy?

Sí. Cada lección de C# Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Fundamentos de P/Invoke
  2. LibraryImport y P/Invoke generado desde el código fuente
  3. Código no seguro, punteros y búferes fijos
  4. Interoperabilidad con COM y wrappers invocables en tiempo de ejecución
← Volver a C# Academy