1

为了学习,我最近查看了一个使用 Win32 WriteFile 的现有程序集(使用 Reflector)。实现是:

Write(IntPtr handleFile, void* bufferData, uint length){
void* buffer = bufferData
while (length > 0)
{
  uint wrtn;
  if (!WriteFile(handle, buffer, len, out wrtn, IntPtr.Zero))
  {
     // Do some error handling
  }
  // This does not compile, because of the cast but also simply because void* does not have += operators (it is unknown size).
  buffer += (void*)wrtn;
  len -= wrtn;
}

}

实际上是最后两行有问题......首先,编译器抱怨您不能将 uint 强制转换为 void*。此外,不可能在 void* 上使用 += 甚至 + ,因为它的大小未知。

Write(IntPtr handleFile, void* bufferData, uint length){
    byte* buffer = (byte*)bufferData
    while (length > 0)
    {
      uint wrtn;
      if (!WriteFile(handle, (void*)buffer, len, out wrtn, IntPtr.Zero))
      {
         // Do some error handling
      }
      // This works! I can add to a byte*
      buffer = buffer + wrtn; // I could also have used buffer += wrtn
      len -= wrtn;
    }
}

上面的代码确实有效,但最后几行仍将编译为:

buffer += (byte*)wrtn;

我不明白为什么并且非常想知道为什么编译器会以这种方式运行:

  1. 为什么它会生成这样的演员表(为什么不接受在用户编写的代码中这样做)?
  2. 第一个示例中 void* 上的 += 运算符是怎么回事?哪些原始代码代码生成了缓冲区 += (void*)wrtn 其中缓冲区也是 void* ????
4

2 回答 2

1

好吧,对于您的第二点, void* 没有大小信息,因此编译器不知道将指针增加多少。它应该增加 sizeof(double) 吗?只有有了类型信息,它才知道会发生什么。

编辑:实际上这也适用于您的第一点。编译器需要知道其递增的数据类型的大小。Void* 不存在大小信息,因此通过转换为字节*,您可以让编译器知道它需要将指针增加 sizeof(byte) * wrtn。

Edit2:通过您的澄清,您似乎在问为什么反射器将代码作为 void* 而不是正确转换的类型(字节 *)发出代码。这很可能是由于从参数类型中提取的类型信息以及在方法中使用的有点天真。这可能是 Reflector 的问题,而不是编译器。

这个指针代码也有可能在 IL 中丢失了它的类型信息,我还没有测试它,但它可能不会将类型信息(除了数据大小)携带到 IL 中,以便 Reflector 正确发出(正常' safe' IL 应该总是有这种类型的信息)。如果是这种情况,Reflector 可能会默认为 void* 或最接近的推断类型。

于 2009-11-02T18:17:57.650 回答
0

出于学习的目的,我最近查看了一个现有的程序集(使用反射器)

这里唯一的问题是使用 Reflector - 显然,它不太擅长从 IL 推导出原始 C# 代码。IL 本身是正确的,并且没有强制转换(不需要 - 在 IL 中,您将指针和整数参数压入堆栈,然后进行加/减)。反光板不对。

于 2009-11-02T18:35:35.217 回答