brickner
brickner

Reputation: 6585

Why must fixed size buffers (arrays) be declared unsafe?

Let's say I want to have a value type of 7 bytes (or 3 or 777).

I can define it like that:

public struct Buffer71
{
    public byte b0;
    public byte b1;
    public byte b2;
    public byte b3;
    public byte b4;
    public byte b5;
    public byte b6;
}

A simpler way to define it is using a fixed buffer

public struct Buffer72
{
    public unsafe fixed byte bs[7];
}

Of course the second definition is simpler. The problem lies with the unsafe keyword that must be provided for fixed buffers. I understand that this is implemented using pointers and hence unsafe.

My question is why does it have to be unsafe? Why can't C# provide arbitrary constant length arrays and keep them as a value type instead of making it a C# reference type array or unsafe buffers?

Upvotes: 32

Views: 12480

Answers (3)

Pika
Pika

Reputation: 51

Beginning with C# 12, you can declare inline arrays as a struct type:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
    private char _firstElement;
}

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#inline-arrays

Upvotes: 5

J Mor
J Mor

Reputation: 185

Just as a note for those finding this now.

As of C#10, you can directly define and assign fields on a struct. Combined with the readonly modifier, this allows you to (almost) guarantee an immutable reference to a mutable reference type (or mutable struct type), including arrays.

public struct Test
{
    public readonly int[] fixedSizeArray = new int [5];
    /// <summary>I'm important, don't forget me.</summary>
    public Test() {}
}

The only caveats being:

  1. Just like with all readonly fields, I believe this can be modified as much as desired in the constructor (which shouldn't be too big a problem as long as you're careful to never reassign this in your constructors as I believe structs can't be derived from).
    • In order to use this properly, I believe you have to explicitly define the parameterless constructor, as failure to do so means for all instances using the implicit public parameterless constructor, all fields will be set to their default uninitialized values (e.g. int i; would be 0, any reference type - like an array - would be null), ignoring the explicit initializers (for some reason, I'm not quite sure). That being said, declaring the public parameterless constructor (also introduced in C#10) and leaving it empty should result in the desired behavior (at least if I'm reading the docs and feature proposal right). So for our example from earlier to work, it must have that (seemingly) useless parameterless constructor. This won't work:
// THIS WON'T WORK
public struct Test
{
    public readonly int[] fixedSizeArray = new int[5];
}
  1. Using the default operator will always skip any and all constructors, so people can theoretically create an instance of the struct without the correct size. That being said, the field is still readonly, so I'm pretty sure no one's gonna be able to pull a fast one and use it incorrectly, as they'll be stuck with a null array. That being said, it's still worth noting as an edge case.

For buffers, you can combine this with Memory and get pretty close. If you don't want unsafe code but want a C++esque buffer, it's likely your best option.

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 942040

Because a "fixed buffer" is not a real array. It is a custom value type, about the only way to generate one in the C# language that I know. There is no way for the CLR to verify that indexing of the array is done in a safe way. The code is not verifiable either. The most graphic demonstration of this:

using System;

class Program {
    static unsafe void Main(string[] args) {
        var buf = new Buffer72();
        Console.WriteLine(buf.bs[8]);
        Console.ReadLine();
    }
}
public struct Buffer72 {
    public unsafe fixed byte bs[7];
}

You can arbitrarily access the stack frame in this example. The standard buffer overflow injection technique would be available to malicious code to patch the function return address and force your code to jump to an arbitrary location.

Yes, that's quite unsafe.

Upvotes: 14

Related Questions