为什么固定大小的缓冲区(数组)必须是不安全的?
问题描述:
比方说,我想有一个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;
}
一种更简单的方式来定义它是使用一个固定的缓冲器
public struct Buffer72
{
public unsafe fixed byte bs[7];
}
课程的第二定义是更简单的。问题在于必须为固定缓冲区提供不安全的关键字。我明白这是使用指针实现的,因此是不安全的。我的问题是为什么它不得不不安全?为什么C#不能提供任意长度的数组,并将它们保留为值类型而不是使其成为C#引用类型的数组或不安全的缓冲区?
答
因为“固定缓冲区”不是真正的数组。它是一种自定义值类型,它是我知道的用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];
}
在本例中,您可以任意访问堆栈帧。标准缓冲区溢出注入技术可用于恶意代码修补函数返回地址并强制您的代码跳转到任意位置。
是的,这是相当不安全的。
因此,CIL缺乏执行有界索引操作的任何方法的问题就是这样吗?我没有看到CIL无法提供这种功能的任何语义原因。一些像图形转换这样的东西可能会超过结构的“理想”16字节大小,但它们在逻辑上应该具有可变的值语义。不可变的语义使得调整实例中的值变得很痛苦,并且可变引用语义在例如引用时引入了模糊性。返回实例的函数将返回一个新实例或一个现有实例。 – supercat 2012-06-09 21:55:44
它并不那么简单,它引发了很多与并发安全保证有关的问题。 – user1496062 2015-05-18 05:34:08
在结构体中嵌入一个固定大小的数组没有安全的方法,这是疯狂的。对于代码的高性能部分,我想要使用几乎100%的可用结构。至少我们现在有ref ref和ref locals。 – JBeurer 2017-11-16 20:45:09