指针算术和 C# 编译器

发布于 2024-08-09 12:15:32 字数 1286 浏览 5 评论 0原文

为了学习的目的,我最近查看了一个使用 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* ???

For the purpose of learning I recently looked at an existing assembly (using Reflector) that uses Win32 WriteFile. The implementation is:

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

}

It is actually the last 2 lines that are problematic... For one, the compiler complains that you cannot cast uint to void*. Additionally, it is just not possible to use += or even + on void* because it is not of known size.

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

The above code does work but still the last few lines will compile to:

buffer += (byte*)wrtn;

I do not understand why and very much would like to know why the compiler behaves in this way:

  1. Why does it generate the cast like this (and why is it not accepted to do this in user written code)?
  2. What is up with the += operators on void* in the first example? What original code code have generated buffer += (void*)wrtn where buffer is also void* ????

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

只有一腔孤勇 2024-08-16 12:15:32

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

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

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

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

Well for your second point, void* has no size information so the compiler doesn't know how much to increment the pointer by. Should it increment by sizeof(double)? Only with type information does it know what to expect.

Edit: Actually this applies to your first point as well. The compiler needs to know the size of the data type its incrementing. Void* has no size information present so by casting to a byte* you let the compiler know that it needs to increment the pointer by sizeof(byte) * wrtn.

Edit2: With your clarification, it appears you're asking why the Reflector emits code as a void* instead of as it's properly casted type (byte *). Most likely this is due to the type information being extracted from the parameter type and somewhat naively being used in the method. This is probably an issue with Reflector more than the compiler.

It's also possible that this pointer code loses it's type information in the IL, I haven't tested it yet, but it may not carry typing information (besides the size of the data) into the IL for the Reflector to properly emit (normal 'safe' IL should always have this type information). If this was the case Reflector may default to void* or the nearest inferred type.

醉酒的小男人 2024-08-16 12:15:32

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

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

For the purpose of learning I recently looked at an existing assembly (using Reflector)

The only problem here is using Reflector - apparently, it's not so good at deducing the original C# code from IL. The IL itself is correct, and has no casts (none are needed - in IL, you push a pointer and an integer argument on the stack, and do an add/subtract). Reflector is wrong.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文