C# Academy · Lektion

Usikker kode, pointere og faste buffere

Brug nøgleordet unsafe, arbejd med pointere, fastlås administreret hukommelse med fixed, og få adgang til buffere med fast størrelse i structs.

Lektion 3 af 412 trin

Usikker kode, pointere og faste buffere er en gratis C# Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i C# Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad er unsafe-kode?

C#'s unsafe-nøgleord åbner for direkte manipulation af hukommelse: rå pointere, pointeraritmetik og buffere med fast størrelse. Det omgår GC'ens sikkerhedsgarantier. Aktivér det med <AllowUnsafeBlocks>true</AllowUnsafeBlocks> i din projektfil.

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

Pointertyper i C#

Pointersyntaksen følger C/C++: T* er en pointer til T. Du kan erklære pointere til unmanaged-værdityper (int, double og strukturer uden referencefelter). Pointere til administrerede typer er ikke tilladt.

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

fixed-sætningen

Administrerede objekter kan flyttes af GC'en. Hvis du vil tage adressen på et administreret objekt, skal du fastlåse det med fixed. GC'en flytter ikke objektet, så længe fixed-blokken varer.

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

Pointeraritmetik

Du kan lægge heltal til pointere eller trække dem fra. Hvis du lægger 1 til en int*, flyttes den 4 bytes frem (sizeof int). Det er grundlaget for hurtige massehukommelsesoperationer.

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: Stakallokering

stackalloc allokerer en hukommelsesblok på stakken (ikke heapen). Ingen belastning af GC'en og ingen fastlåsning er nødvendig. Det er begrænset til den aktuelle metodes stakramme — hukommelsen frigives automatisk, når metoden returnerer.

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

Buffere med fast størrelse i strukturer

En buffer med fast størrelse indlejrer et array med fast længde direkte i en struktur — ingen heap-allokering og ingen ekstra pointer. Brug modifikatoren fixed inde i en struktur, der er erklæret i en unsafe-kontekst.

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* og typekonvertering

En void* er en typeløs pointer — nyttig til generiske hukommelsesoperationer. Du skal konvertere den til en pointer med en bestemt type, før du derefererer den. Operatoren sizeof(T) fungerer på unmanaged-typer i en unsafe-kontekst.

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 og Unsafe-klassen

System.Runtime.CompilerServices.Unsafe og System.Runtime.InteropServices.MemoryMarshal tilbyder sikre alternativer uden nøgleordet til mange operationer på lavt niveau. De er kompatible med Span og kan bruges uden nøgleordet 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

Hvornår du ikke skal bruge unsafe

Brug kun unsafe-kode, når der er et klart behov for ydeevne, og der ikke findes et sikkert alternativ. Hold unsafe-området minimalt — isolér det i private hjælpemetoder eller dedikerede typer. Skriv grundige test, og dokumentér antagelserne.

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

Fra virkeligheden: Parsing af en binær protokol

Unsafe-pointere og buffere med fast størrelse er særligt nyttige ved parsing af binære protokoller, hvor strukturens layout skal matche et wireformat nøjagtigt. Strukturen kan castes direkte fra en bytebuffer uden kopiering.

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

Hurtigt tjek

Hvorfor er fixed-sætningen nødvendig, når du tager adressen på et administreret objekt?

Opsummering: Unsafe-kode, pointere og faste buffere

Vigtigste pointer:

  • Nøgleordet unsafe åbner for rå pointere — kræver AllowUnsafeBlocks i projektet
  • fixed-sætningen fastlåser administrerede objekter, så GC'en ikke flytter dem
  • Pointeraritmetik flytter sig med elementets størrelse, ikke med antallet af bytes
  • stackalloc allokerer på stakken — ingen belastning af GC'en, og hukommelsen frigives, når metoden afsluttes
  • Buffere med fast størrelse (fixed byte Payload[N]) indlejrer arrays direkte i strukturer
  • Foretræk klasserne Span<T>, MemoryMarshal og Unsafe i de fleste scenarier
Gratis at komme i gang

Lær C# med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
93
Lektioner
346

Ofte stillede spørgsmål

Er lektionen “Usikker kode, pointere og faste buffere” gratis?

Ja — hele teksten til “Usikker kode, pointere og faste buffere” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af C# Academy-kurset, skal du opgradere til CoddyKit PRO. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Usikker kode, pointere og faste buffere”?

Brug nøgleordet unsafe, arbejd med pointere, fastlås administreret hukommelse med fixed, og få adgang til buffere med fast størrelse i structs. Du øver dig i C# Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på C# Academy?

Der kræves ingen tidligere erfaring. C# Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Usikker kode, pointere og faste buffere”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne C# Academy-lektion?

Ja. Alle C# Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Grundlæggende om P/Invoke
  2. LibraryImport og source-genereret P/Invoke
  3. Usikker kode, pointere og faste buffere
  4. COM-interoperabilitet og runtime-callable wrappers
← Tilbage til C# Academy