Onveilige code, pointers en vaste buffers
Gebruik het sleutelwoord unsafe, werk met pointers, pin beheerd geheugen met fixed en krijg toegang tot buffers met een vaste grootte in structs.
Onveilige code, pointers en vaste buffers is een gratis C# Academy-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject C# Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus C# Academy bevat in totaal 4 lessen.
Wat is unsafe code?
Het sleutelwoord unsafe van C# maakt directe geheugenbewerking mogelijk: ruwe pointers, pointerberekeningen en buffers met een vaste grootte. Het omzeilt de veiligheidsgaranties van de GC. Schakel het in met <AllowUnsafeBlocks>true</AllowUnsafeBlocks> in je projectbestand.
// 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
}Pointertypen in C#
De pointersyntaxis komt overeen met die van C/C++: T* is een pointer naar T. Je kunt pointers declareren naar onbeheerde waardetypen (int, double en structuren zonder referentievelden). Pointers naar beheerde typen zijn niet toegestaan.
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
}
}De fixed-instructie
Beheerde objecten kunnen door de GC worden verplaatst. Om het adres van een beheerd object op te vragen, moet je het met fixed vastzetten. De GC verplaatst het object niet zolang het fixed-blok actief is.
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]); // 5Pointerberekeningen
Je kunt gehele getallen bij pointers optellen of ervan aftrekken. Als je 1 optelt bij een int*, gaat de pointer 4 bytes vooruit (sizeof int). Dit vormt de basis voor snelle bewerkingen op grote geheugenblokken.
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: toewijzing op de stack
stackalloc wijst een geheugenblok toe op de stack (niet op de heap). Geen belasting voor de GC en geen vastzetten nodig. De toewijzing blijft beperkt tot het stackframe van de huidige methode — het geheugen wordt automatisch vrijgegeven wanneer de methode terugkeert.
// 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 goneBuffers met vaste grootte in structuren
Een buffer met vaste grootte bevat een array met vaste lengte rechtstreeks in een struct — geen toewijzing op de heap en geen extra pointer. Gebruik de fixed-modifier binnen een struct die in een unsafe-context is gedeclareerd.
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* en typeconversie
Een void* is een pointer zonder type — handig voor algemene geheugenbewerkingen. Je moet deze naar een pointer met type converteren voordat je hem dereferereert. De operator sizeof(T) werkt in een onveilige context op onbeheerde typen.
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
}De klassen MemoryMarshal en Unsafe
System.Runtime.CompilerServices.Unsafe en System.Runtime.InteropServices.MemoryMarshal bieden veilige alternatieven waarvoor je geen trefwoord nodig hebt voor veel bewerkingen op laag niveau. Ze zijn compatibel met Span en kunnen zonder het trefwoord unsafe worden gebruikt.
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]); // 99999Wanneer je Unsafe wel en niet gebruikt
Gebruik onveilige code alleen wanneer daar een duidelijke prestatiebehoefte voor is en er geen veilig alternatief bestaat. Houd het onveilige oppervlak klein — isoleer het in private helpermethoden of speciale typen. Schrijf uitgebreide tests en documenteer je aannames.
// 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 perfPraktijkvoorbeeld: een binair protocol parseren
Onveilige pointers en buffers met een vaste grootte zijn bijzonder geschikt voor het parseren van binaire protocollen waarbij de struct-indeling exact moet overeenkomen met het wireformaat. De struct kan rechtstreeks vanuit een bytebuffer worden geconverteerd, zonder kopieën.
[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
}Korte controle
Waarom is de instructie fixed nodig wanneer je het adres van een beheerd object opvraagt?
Samenvatting: onveilige code, pointers en buffers met vaste grootte
Belangrijkste punten:
- Het sleutelwoord
unsafemaakt ruwe pointers mogelijk — hiervoor isAllowUnsafeBlocksin het project vereist - De instructie
fixedzet beheerde objecten vast, zodat de GC ze niet verplaatst - Pointerberekeningen gaan vooruit met de elementgrootte (niet met bytes)
stackallocwijst geheugen toe op de stack — geen belasting voor de GC en vrijgave bij het verlaten van de methode- Buffers met vaste grootte (
fixed byte Payload[N]) bevatten arrays rechtstreeks in structuren - Geef in de meeste situaties de voorkeur aan de klasse
Span<T>,MemoryMarshalenUnsafe
Leer C# met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 93
- Lessen
- 346
Veelgestelde vragen
Is de les “Onveilige code, pointers en vaste buffers” gratis?
Ja — de volledige tekst van “Onveilige code, pointers en vaste buffers” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus C# Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus C# Academy bevat in totaal 4 lessen.
Wat leer ik in “Onveilige code, pointers en vaste buffers”?
Gebruik het sleutelwoord unsafe, werk met pointers, pin beheerd geheugen met fixed en krijg toegang tot buffers met een vaste grootte in structs. Je oefent met C# Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met C# Academy te beginnen?
Ervaring vooraf is niet nodig. C# Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.
Hoe lang duurt de les “Onveilige code, pointers en vaste buffers”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over C# Academy?
Ja. Elke les over C# Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Basisprincipes van P/Invoke
- LibraryImport en source-generated P/Invoke
- Onveilige code, pointers en vaste buffers
- COM-interoperabiliteit en Runtime Callable Wrappers