Search code examples
c#.netpointersunsafestackalloc

Why does stackalloc have to be used as a variable initializer?


I'm writing some unsafe code in C# (follow-up to this question) and I'm wondering, why exactly does the stackalloc keyword have to be used as a variable initializer? e.g. This will produce a syntax error:

public unsafe class UnsafeStream
{
    byte* buffer;

    public UnsafeStream(int capacity)
    {
        this.buffer = stackalloc byte[capacity]; // "Invalid expression term 'stackalloc' / ; expected / } expected"
    }
}

But re-assigning the results from a local temporary will not:

public UnsafeStream(int capacity)
{
    byte* buffer = stackalloc byte[capacity];
    this.buffer = buffer;
}

Why isn't the first version allowed, and what evil things will happen if I attempt the second version?


Solution

  • Your stack is looking something very roughly like this:

    [stuff from earlier calls][stuff about where this came from][this][capacity]
                                                                       ^You are here
    

    Then you do stackalloc and this adds two things to the stack, the pointer and the array pointed to:

    [stuff from earlier calls][stuff about where this came from][this][capacity][buffer][array pointed to by buffer]
                                                                                                ^You are here
    

    And then when you return the stuff most recently put on the stack, the locals of the current function, its return address, and the stackalloced buffer are all simply ignored (which is one of the advantages of stackalloc, ignoring stuff is fast and easy):

    [stuff from earlier calls][stuff about where this came from][this][capacity][buffer][array pointed to by buffer]
                           ^You are here
    

    It can be overwritten by the next method call:

    [stuff from earlier calls][stuff about where this came from][this][new local1][new local2]o by buffer]
                                                                                     ^You are here
    

    What you are proposing, is that a private field, which is to say a part of an object on the heap (a different piece of memory, managed differently) hold a pointer to the buffer that has been half-overwritten by completely different data, of different types.

    Immediately consequences would be:

    1. Attempts to use buffer are now fraught because half of it is overwritten by item, most of which aren't even bytes.
    2. Attempts to use any local is now fraught, because future changes to buffer can overwrite them with random bytes in random places.

    And that's just considering the single thread involved here, never mind other threads with separate stacks perhaps being able to access that field.

    It's also just not very useful. You can coerce a field to hold an address to somewhere on a stack with enough effort, but there isn't that much good one can do with it.