2009-11-02 3 views
1

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

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 *, потому что это не известный размер.

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, где буфер также недействителен * ????

ответ

1

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

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

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

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

+0

Спасибо за ответ, но, как я уже указывал в своем оригинальном посте, я знаю о всей вещи «неизвестного» размера. Меня удивляет то, что компилятор все равно каким-то образом генерирует void * somepointer + = (void *) uint wrtn (я добавил тип для ясности). Это не должно быть возможным (и, конечно же, не компилируется!), Поэтому мне интересно, какой исходный код был сгенерирован этим ... – Kris

+0

Так что это скорее всего отражение.В конце концов, задача Reflectors, вероятно, не простая. Я мог понять, как рефлектор выплескивает броски на основе найденного ИЛ, так что часть вопроса, которую я считаю, решена. Тем не менее, я пытался и пытался генерировать вывод отражателя (ради получения проницательности) точно так же, как тот, который был в моем самом первом блоке кода (поэтому без использования байта *), и я просто не могу туда добраться ... Вы не можете сделать много с void *, поэтому мне действительно интересно, как выглядел бы оригинальный код, который сгенерировал это! – Kris

+0

Скорее всего, это была ваша «фиксированная» копия (в пределах некоторого допуска). Вы пытались сбросить IL и отсортировать это вручную? Нетрудно понять, с чем должен работать Reflector, чтобы вы могли видеть, откуда это взялось. –

0

Для целей обучения Недавно я смотрел на существующую сборку (с помощью рефлектора)

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

+0

Итак, какой код мог сгенерировать буфер + = (void *) wrtn ???? Я могу представить, что рефлектор ошибочен, но я не могу написать код, который будет генерировать линию, только что упомянутую в рефлекторе ... И таким образом мой вопрос остается без ответа :) – Kris

+0

Попробуйте 'buffer = (byte *) buffer + wrtn'. –