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

rez*_*eza 3 c# pointers fixed

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

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

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

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

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

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

为什么这种行为上的差异?

---编辑-----

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

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

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

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

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

Ben*_*igt 5

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

但这是C#.数组和指针是完全独立的动物(尽管你可以将数组强制转换为指针,但你必须在fixed语句中这样做,以确保指针保持有效).你应该比较

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

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

两者都使用数组.要不然

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

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

两者都使用指针.

然后你会看到变量声明的范围与需要完全没有关系fixed.

  • 第二种情况不会编译,并发出警告说我需要在那里添加一个固定的.第三种情况也将失败,说我不能将修复语句应用于固定指针(注意raw在结构中是固定的). (2认同)
  • 固定缓冲区不是数组.这个问题根本没有显示数组. (2认同)

Mar*_*ell 5

不幸的是,你稍微混淆了这个问题。如果我准确地复制问题,那么效果很好:

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

和这个:

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

提高:

不能使用固定语句来获取已经固定的表达式的地址

如果我们删除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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在意思变得更清楚了。您会看到,当您用于fixed访问值中的固定缓冲区时,您实际上并没有修复缓冲区,也没有修复该;您实际上要修复的是包含对象,即具有该字段的对象r。这是为了防止 GC 在堆栈上移动它,如果我们当时将它作为指针访问,这会很糟糕。在上面的示例中,我们的表达式是 real fixed (byte* ptr = this.r.raw),固定的内容是:this

如果我们只有一个结构作为本地结构,情况会有所不同。本地人在堆栈上;它们(正如之前的消息所暗示的)已经修复了;GC 永远不会重新定位堆栈。

所以:

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