Search code examples
c#pointersvoid-pointers

pointer arithmetic and the C# compiler


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* ????

Solution

  • 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.