指针算术和 C# 编译器

问题描述 投票:0回答:2

为了学习的目的,我最近查看了一个使用 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* 上的 += 运算符是怎么回事?哪些原始代码生成了 buffer += (void*)wrtn 其中 buffer 也是 void* ????
c# pointers void-pointers
2个回答
1
投票

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

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

Edit2:经过您的澄清,您似乎在问为什么 Reflector 将代码发出为 void* 而不是正确转换的类型(字节 *)。 这很可能是因为类型信息是从参数类型中提取的,并且在方法中有些天真地使用了。 这可能是 Reflector 的问题,而不是编译器的问题。

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


0
投票

为了学习的目的,我最近查看了一个现有的程序集(使用 Reflector)

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

© www.soinside.com 2019 - 2024. All rights reserved.