Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么固定大小的缓冲区只能是基元类型?_C#_Unsafe - Fatal编程技术网

C# 为什么固定大小的缓冲区只能是基元类型?

C# 为什么固定大小的缓冲区只能是基元类型?,c#,unsafe,C#,Unsafe,我们必须经常与本机代码进行互操作,在这种情况下,使用不需要封送的不安全结构要快得多。但是,当结构包含非基本类型的固定大小缓冲区时,我们不能这样做。 为什么C#编译器要求固定大小的缓冲区只属于原语类型?为什么固定大小的缓冲区不能由以下结构组成: [StructLayout(LayoutKind.Sequential)] struct SomeType { int Number1; int Number2; } 我理解你的观点……另一方面,我认为这可能是微软保留的某种前向兼容性。您的代码被

我们必须经常与本机代码进行互操作,在这种情况下,使用不需要封送的不安全结构要快得多。但是,当结构包含非基本类型的固定大小缓冲区时,我们不能这样做。 为什么C#编译器要求固定大小的缓冲区只属于原语类型?为什么固定大小的缓冲区不能由以下结构组成:

[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
  int Number1;
  int Number2;
}

我理解你的观点……另一方面,我认为这可能是微软保留的某种前向兼容性。您的代码被编译为MSIL,在内存中布局它是特定的.NET Framework和操作系统的业务

我可以想象,它可能来自intel的新CPU,需要将变量布局为每8字节一次,以获得最佳性能。在这种情况下,在未来的.NETFramework6和Windows9中,需要以不同的方式布局这些结构。在这种情况下,您的示例代码将给Microsoft带来压力,使其在将来不要更改内存布局,也不要将.NET framework加速到现代硬件

这只是猜测

你试过设置FieldOffset吗?请参见

什么是固定缓冲区? 从MSDN:

在C#中,可以使用fixed语句在数据结构中创建具有固定大小数组的缓冲区。这在处理现有代码时非常有用,例如用其他语言编写的代码、预先存在的DLL或COM项目。固定数组可以采用常规结构成员所允许的任何属性或修饰符。唯一的限制是数组类型必须是bool、byte、char、short、int、long、sbyte、ushort、uint、ulong、float或double

我想引用Hans Passant先生的话,解释一下为什么固定缓冲区必须是不安全的。您可以查看更多信息

因为“固定缓冲区”不是真正的数组。这是一种自定义值类型,大约是唯一的方法 用我熟悉的C语言生成一个。这是不可能的 CLR验证数组的索引是否以安全的方式完成。 代码也不可验证。最生动的演示 这:

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

是的,那很不安全

为什么固定缓冲区不能包含非基本数据类型? 西蒙·怀特提出了一个有效的观点:

我要说的是“增加了编译器的复杂性”。编译器必须检查应用于可枚举项的结构是否未应用.NET特定功能。例如,泛型、接口实现、甚至非原语数组的更深层属性等等。毫无疑问,运行时也会有一些互操作问题

和Ibasa:


“但这已经由编译器完成了。”只是部分。编译器可以进行检查,以查看是否管理了类型,但这并不负责生成代码以将结构读/写到固定缓冲区。它是可以做到的(在CIL级别没有什么可以阻止它),只是没有在C#中实现

最后,Mehrdad:

我认为这实际上是因为他们不希望您使用固定大小的缓冲区(因为他们希望您使用托管代码)。使与本机代码的互操作变得太容易会使您不太可能将.NET用于所有事情,而且他们希望尽可能多地提升托管代码

答案似乎是响亮的“它只是没有实施”

为什么没有实施? 我的猜测是,成本和实施时间对他们来说并不值得。开发人员更愿意推广托管代码而不是非托管代码。这可能在未来版本的C#中实现,但当前的CLR缺乏所需的复杂性

另一个选择可能是安全问题。由于固定缓冲区极易受到各种各样的问题和安全风险的影响,如果它们在代码中实现得不好,我可以理解为什么在C#中,使用它们会比使用托管代码更令人沮丧。为什么要在一些不鼓励使用的东西上投入大量工作?

C#中的固定大小缓冲区是通过一个名为“不透明类”的CLI功能实现的。的第I.12.1.6.3节对其进行了描述:

有些语言提供多字节数据结构,其内容由 地址算术和间接操作。为了支持此功能,CLI允许值类型 以指定的大小创建,但不包含有关其数据成员的信息。实例 这些“不透明类”的处理方式与任何其他类的实例完全相同,但 不得使用ldfld、stfld、ldflda、ldsfld和stsfld指令访问其内容

“无关于其数据成员的信息”和“不得使用ldfld/stfld”是关键。第二条规则将kibosh放在结构上,您需要ldfld和stfld来访问它们的成员。C#编译器无法提供替代方案,结构的布局是运行时实现细节。这也是不能在结构上使用sizeof运算符的原因。Decimal和Nullable也被排除在外,因为它们也是结构。IntPtr不可用,因为它的大小取决于进程的位,这使得C#编译器很难生成用于访问缓冲区的ldind/stind操作码的地址。引用类型引用被删除,因为GC需要能够找到它们,并且根据第一条规则无法找到它们。枚举类型的大小根据其基类型而变化;听起来像是一个可以解决的问题,但不完全确定他们为什么跳过它

只剩下C语言规范中提到的:sbyte,byte,short,ushort,int,
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];
}