【问题标题】:fixed statement in C# and managed pointer in IL codeC# 中的固定语句和 IL 代码中的托管指针
【发布时间】:2011-06-25 20:54:27
【问题描述】:

在 C# 的不安全代码中,我将指针分配给数组类型的托管变量:

int[] array = new int[3];
...
fixed (int* ptr = array)
{
    //some code
}

然后我查看了IL代码的相应部分:

.locals init ([0] int32[] 'array',
       [1] int32& pinned ptr)

我想知道,既然这是不安全的代码,并且int* ptr 是非托管指针的声明(或者我现在这么认为),为什么在 IL 代码中不写 int32* ptr,而不是 int32& ptr ?

【问题讨论】:

    标签: c# pointers unmanaged cil fixed


    【解决方案1】:

    http://www.ecma-international.org/publications/standards/Ecma-335.htm

    第 334 页

    《1.1.5.2 托管指针(类型&)

    1.2 托管指针(&)可以指向一个局部变量、一个方法参数、一个 对象的字段,值的字段 类型,数组元素,静态 字段,或元素所在的地址 刚刚超过数组的末尾将是 存储(用于指针索引到 托管数组)。托管指针 不能为空。 (他们应该是 报告给垃圾收集器, 即使他们不指向托管 记忆)”

    第 149 页

    7.1.2 固定

    pinned 的签名编码应仅出现在描述局部变量的签名中(第 15.4.1.3 节)。 当一个带有固定局部变量的方法正在执行时,VES 不应重新定位该局部变量所引用的对象。也就是说,如果 CLI 的实现使用移动对象的垃圾收集器,则收集器不应移动由活动固定局部变量引用的对象。 [基本原理:如果非托管指针用于取消引用托管对象,则这些对象应被固定。例如,当托管对象被传递给设计为使用非托管数据操作的方法时,就会发生这种情况。结束理由]

    我同意 Hans 关于 msil 语言设计选择背后的理性的观点。


    这两件事是不同的:

    int[] arry = new int[5];
    
    fixed (int* ptr = arry)
    {
      ...
    }
    

    对比

    int* ptr = stackalloc int[5];
    

    如果您查看为第二个创建的 IL,您会看到以下内容(我认为这是您所期望的):

    .locals init ([0] int32* ptr)
    

    在第一个版本(您的版本)中,您指向 System.Array 的一个实例(托管类型)。在我的版本中(使用 stackalloc),您指向的是我认为您期望指向的内容……一块足够大的内存块可容纳 5 个整数。

    【讨论】:

    • 我不明白其中的原理:“如果使用非托管指针取消引用托管对象,则应固定这些对象”。在上面的代码中,数组被固定语句固定,我们还在使用托管指针吗?
    • @Steve 感谢您抽出宝贵时间。我可以注意到,在您使用 stackalloc int[5] 的示例中,我们占用了堆栈上的内存,而不是堆上的内存,就像使用 new int[5] 一样,所以现在这不受垃圾收集的影响,我们不需要修复语句,因为 C# 编译器只允许您在固定语句中分配指向托管变量的指针。那么,当我在固定语句中将指针分配给托管变量时,该指针总是被声明为托管指针吗?我可以接受:)
    • @Vlad... 你明白了。如果您不介意检查投票箭头下的复选标记,我将不胜感激:)
    • @Steve 我已经在 30 分钟前或其他什么时候检查过它,但还没有去 :)。还有一件事。我想在你的例子中指出一些东西 int* ptr = stackalloc int[5];在 IL 代码中,您得到 .locals init ([0] int32* ptr) 无论您是否为声明的变量 ptr 赋值。如果你只写 int* ptr;你仍然会得到 .locals init ([0] int32* ptr)。如果你引用了垃圾收集堆上的某些东西,你不能只写 int* ptr = new int[5];您必须在固定语句中声明 ptr 并分配对 ptr 的引用。
    • 对我来说不太清楚,但我只是编辑了 CIL 代码并写了 * 而不是 &,并且代码执行时没有抱怨。 #%$%$@我要睡觉了:)
    【解决方案2】:

    Ildasm 是由 Microsoft 的 C++ 程序员编写的。一种语言,指针和引用之间的区别很大。引擎盖下的 C++ 引用也是一个指针,但它保证永远不会为空。引用在语法上由 & 标识,指针是 *。

    这里就是这样,指针和指向的值是有区别的。指向的值可能为空,但对指针的引用永远不会为空。 “数组”变量保证存在于堆栈帧中,因此其引用具有非空值。只有它的 value 可能为空。当数组未初始化时会发生这种情况。这种间接级别使得指针在 C# 语言中不受欢迎并且基本上不存在。还有 CS101。

    【讨论】:

    • 呵呵,我没看懂:在CLR中对对象的引用是一回事,托管指针是第二,非托管指针是第三。例如这个不安全的 c# 代码: int x;整数* y; y=&x;在 IL 代码中,我们将有 .locals init (int32* y) 就像预期的那样,我可以说,因为我声明了 x,&x 永远不会为空,x 的值可以为空,但我没有看到该事实与局部变量 y 被声明为托管或非托管指针的方式之间的联系?
    【解决方案3】:

    c# 是一种托管编码语言,因此不会有任何指针。但是有一个包装类来使用指针可能是因为你注意到这两者有一些不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      • 2013-01-05
      • 2021-04-09
      • 1970-01-01
      • 2012-10-10
      • 2011-11-11
      相关资源
      最近更新 更多