арифметика указателей и компилятор C # - PullRequest
1 голос
/ 02 ноября 2009

В целях обучения я недавно посмотрел на существующую сборку (используя Reflector), которая использует Win32 WriteFile. Реализация:

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

}

На самом деле проблемными являются последние 2 строки ... Например, компилятор жалуется, что вы не можете привести 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, где buffer также void * ????

Ответы [ 2 ]

1 голос
/ 02 ноября 2009

Что касается вашего второго пункта, void * не имеет информации о размере, поэтому компилятор не знает, на сколько увеличить указатель. Должен ли он увеличиваться на sizeof (double)? Только с информацией о типе он знает, чего ожидать.

Редактировать: На самом деле это относится и к вашей первой точке. Компилятору необходимо знать размер приращения типа данных. В Void * нет информации о размере, поэтому, приведя к байту *, вы дадите компилятору понять, что ему нужно увеличить указатель на sizeof (byte) * wrtn.

Edit2: с вашим разъяснением, вы, похоже, спрашиваете, почему Reflector испускает код как void *, а не как правильно приведенный тип (байт *). Скорее всего, это связано с тем, что информация о типе извлекается из типа параметра и несколько наивно используется в методе. Это, вероятно, проблема с Reflector больше, чем с компилятором.

Также возможно, что этот код указателя теряет информацию о своем типе в IL, я еще не проверял его, но он может не переносить информацию о наборе (кроме размера данных) в IL для правильного излучения Reflector. (нормальный «безопасный» IL всегда должен иметь эту информацию о типе). В этом случае Reflector может по умолчанию использовать void * или ближайший предполагаемый тип.

0 голосов
/ 02 ноября 2009

В целях обучения я недавно посмотрел на существующую сборку (используя Reflector)

Единственная проблема здесь - использование Reflector - по-видимому, он не так хорош в выводе исходного кода C # из IL. Сам IL корректен и не имеет приведений (ни одного не требуется - в IL вы помещаете указатель и целочисленный аргумент в стек и выполняете сложение / вычитание). Отражатель не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...