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]); // 5Aritmé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 goneBú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 stackvoid* 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]); // 99999Cuá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 perfCaso 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
unsafepermite usar punteros sin procesar; requiereAllowUnsafeBlocksen el proyecto - La instrucción
fixedancla 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
stackallocasigna 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>,MemoryMarshaly la claseUnsafeen 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
- Fundamentos de P/Invoke
- LibraryImport y P/Invoke generado desde el código fuente
- Código no seguro, punteros y búferes fijos
- Interoperabilidad con COM y wrappers invocables en tiempo de ejecución