为什么固定大小的缓冲区(数组)必须是不安全的?

bri*_*ner 25 c# arrays unsafe fixed value-type

假设我想要一个7字节(或3或777)的值类型.

我可以这样定义:

public struct Buffer71
{
    public byte b0;
    public byte b1;
    public byte b2;
    public byte b3;
    public byte b4;
    public byte b5;
    public byte b6;
}
Run Code Online (Sandbox Code Playgroud)

定义它的一种更简单的方法是使用固定缓冲区

public struct Buffer72
{
    public unsafe fixed byte bs[7];
}
Run Code Online (Sandbox Code Playgroud)

当然第二个定义更简单.问题在于必须为固定缓冲区提供unsafe关键字.我知道这是使用指针实现的,因此不安全.

我的问题是为什么它必须是不安全的?为什么C#不能提供任意常量长度数组并将它们保存为值类型而不是使其成为C#引用类型数组或不安全缓冲区?

Han*_*ant 13

因为"固定缓冲区"不是真正的数组.它是一个自定义值类型,关于用我知道的C#语言生成一个的唯一方法.CLR无法验证数组的索引是否以安全的方式完成.代码也不可验证.最具代表性的演示:

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];
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,您可以任意访问堆栈帧.恶意代码可以使用标准缓冲区溢出注入技术来修补函数返回地址并强制您的代码跳转到任意位置.

是的,这是非常不安全的.

  • 问题就在于CIL缺乏执行有界索引操作的方法吗?我没有看到CIL无法提供此类功能的任何语义原因.像图形变换这样的东西可能比结构的"理想"16字节大小稍微多一些,但它们在逻辑上应该具有可变的值语义.不可变语义使得在实例中调整值变得痛苦,并且可变引用语义引入了关于何时例如返回实例的函数将返回新实例或现有实例的模糊性. (13认同)
  • 令人疯狂的是,没有*安全的方法在结构中嵌入固定大小的数组.对于代码的高性能部分,我想要使用几乎100%的blittable结构.至少我们现在有ref ref和ref locals. (3认同)
  • -1 因为没有尝试回答这个问题。问题已经允许当前的实现是不安全的,所以这个答案没有任何价值。它询问为什么必须以不安全的方式实施它,答案并未涉及这一点。事实上,这个问题甚至问为什么 C# 不能*提供*一个实际的数组,这个答案基本上是说,“因为他们不提供一个数组”。 (2认同)