3

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

[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;
}

为什么会有这种行为差异?

--- 已编辑 -----

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

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

生成您不能使用固定语句来获取已经固定表达式的地址并且不会编译。

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

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

4

2 回答 2

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;
}

两者都使用指针。

然后你会看到声明变量的作用域与 needing 完全没有关系fixed

于 2012-10-26T22:45:40.007 回答
5

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

    {
        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,它可以工作。

你应该展示的是函数签名,即

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](因为您可以就地操作数组的内容)
于 2012-10-27T18:39:43.823 回答