“固定”的要求在C#中似乎不一致

时间:2012-10-26 22:22:51

标签: c# pointers fixed

我有一个包含字段的有序结构..

[StructLayout(LayoutKind.Explicit)]
public unsafe struct RunBlock_t {
    [System.Runtime.InteropServices.FieldOffset(0)]  public fixed byte raw[512];
}

如果我在一个函数中声明这个并且想要使用指针,它可以正常工作..

{
  RunBlock_t r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

但如果我在范围之外声明变量,则需要一个固定的实现

RunBlock_t r;
{
  r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<510; i++) ptr[i]=0;
}

为什么会出现这种行为差异?

--- EDITED -----

只想再次声明任何其他排列都不起作用。

    unsafe void foo() {
        RunBlock_t r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 512; i++) ptr[i] = 0;
    }

生成 您不能使用fixed语句获取已修复表达式 的地址,也不能编译。

    RunBlock_t r;
    unsafe void foo() {
      r = new RunBlock_t();
      for (int i=0; i<512; i++) r.raw[i]=0;
    }

生成 您不能使用未固定表达式中包含的固定大小缓冲区。尝试使用fixed语句。 并且不编译。

2 个答案:

答案 0 :(得分:5)

你的问题没有任何意义。您可能会将数组与指针混淆,可能是因为在C和C ++中,数组在大多数情况下都会快速降级为指针,包括[]下标运算符。

但这是C#。数组和指针是完全独立的动物(虽然你可以强制指向一个数组,但你必须在fixed语句中这样做,以确保指针保持有效)。你应该比较

{
  RunBlock_t r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

RunBlock_t r;
{
  r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

两者都使用数组。或者

{
  RunBlock r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}

RunBlock_t r;
{
  r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}

两者都使用指针。

然后您将看到声明变量的范围与需要fixed完全没有关系。

答案 1 :(得分:4)

不幸的是,你有点混淆了这个问题。如果我从问题完全复制,那么这很好用:

    {
        RunBlock_t r = new RunBlock_t();
        for (int i = 0; i < 512; i++) r.raw[i] = 0;
    }

和此:

    RunBlock_t r;
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }

提出:

  

您不能使用fixed语句获取已修复表达式的地址

如果我们移除fixed,它就会有效。

你应该展示的是功能签名,即

RunBlock_t r;
unsafe void Bar()
{
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }
}

现在意义变得更加清晰了。您可以看到,当您使用fixed访问值中的固定缓冲区时,您实际上并未修复缓冲区,也无法修复 ;您实际修复的内容是包含对象,即具有r字段的对象。这是为了防止GC在堆栈上移动它,如果我们当时将它作为指针访问它将是不好的。在上面的示例中,我们的表达式实际上是fixed (byte* ptr = this.r.raw),而固定的东西是:this

如果我们将结构体作为本地结构,则会有所不同。当地人在堆栈;它们(如前面的消息所暗示的那样)已经修复; GC从不重新定位堆栈。

所以:

  • 如果您有一个结构作为局部变量,则不需要使用fixed - 您只是直接将其作为指针访问(通过ldloca
  • 如果您在对象中有是字段的结构,则需要使用fixed,在操作期间将对象固定到位
  • 如果您将引用传递给结构(即ref RunBlock_t参数),则必须使用fixed 以防万一它是物体上的一个场;如果引用结果是解析到堆栈,则它不需要做任何事情
  • 请注意,此处有关“对象上的字段”的所有内容同样适用于“数组中的值”,即如果我们正在讨论someArray[8](因为您可以原位操作数组的内容)< / LI>