Небезопасный метод получения указателя на массив байтов

будет ли это поведение действительным в С#

public class MyClass
{
    private byte[] data;
    public MyClass()
    {
        this.data = new byte[1024];
    }
    public unsafe byte* getData()
    {
        byte* result = null;
        fixed (byte* dataPtr = data)
        {
            result = dataPtr;
        }
        return result;
    }
}

person uray    schedule 09.04.2012    source источник
comment
Скомпилируйте и посмотрите. Это всегда лучший способ узнать, нет смысла спрашивать здесь...   -  person thecoop    schedule 09.04.2012
comment
Зачем тебе это? Пожалуйста, не пытайтесь писать C на C#!   -  person Krizz    schedule 09.04.2012
comment
@thecoop: компиляция покажет вам, является ли код синтаксически верным, а не семантически верным. В данном конкретном случае даже запуск кода не обязательно позволит вам обнаружить, что в нем есть недостатки.   -  person Brian    schedule 09.04.2012


Ответы (5)


Если вы собираетесь отключить систему безопасности, то вы несете ответственность за обеспечение безопасности памяти программы. Как только вы это сделаете, вы должны сделать все безопасно, без помощи системы безопасности. Вот что значит "небезопасно".

Как ясно сказано в спецификации С#:

адрес подвижной переменной можно получить только с помощью фиксированного оператора, и этот адрес остается действительным только в течение этого фиксированного оператора.

Вы получаете адрес перемещаемой переменной, а затем используете его по истечении срока действия фиксированного оператора, поэтому адрес больше не является действительным. Поэтому от вас требуется не делать именно то, что вы делаете.

Вы не должны писать какой-либо небезопасный код, пока у вас не будет полного и глубокого понимания правил, которым вы должны следовать. Начните с прочтения всей главы 18 спецификации.

person Eric Lippert    schedule 09.04.2012
comment
Вы неправы. вы можете использовать структуру GCHandle, используя флаг закрепления, чтобы прикрепить объект к его адресу памяти, пока не будет вызвана функция handle.Free(). - person Florian; 11.05.2013
comment
@thefiloe: я знаю об этом. Насколько это актуально, и какое мое предложение вы считаете неправильным? - person Eric Lippert; 11.05.2013

Этот код прекрасно скомпилируется, однако это приведет к проблемам во время выполнения. Код, по сути, выводит указатель на незафиксированный объект в куче. Следующий GC, который перемещает тип MyClass, также перемещает ссылку data вместе с ним, и любые ранее возвращенные значения из getData теперь будут указывать на неправильное местоположение.

var obj = new MyClass();
unsafe byte* pValue = obj.getData();
// Assuming no GC has happened (bad assumption) then this works fine
*pValue = 42;

// Assume a GC has now happened and `obj` moved around in the heap.  The 
// following code is now over writing memory it simply doesn't own
*pValue = 42;

Эта последняя строка вызвала сбой приложения, перезаписала значение string в другом типе или просто вставила значение в неинициализированный массив и просто испортила математическую задачу в другом месте? У тебя нет идей. Лучший результат в том, что код просто быстро дает сбой, но, скорее всего, он сделает что-то гораздо более тонкое и злое.

person JaredPar    schedule 09.04.2012

Вы можете использовать метод Marshal.StructureToPtr() вместо небезопасной магии :)

StructureToPtr копирует содержимое структуры в предварительно выделенный блок памяти, на который указывает параметр ptr.

Метод Marshal.StructureToPtr (Object, IntPtr, Boolean)

person Warlock    schedule 14.12.2013

Этот код не будет работать (он скомпилируется, но во время выполнения вызовет проблемы). После окончания фиксированного региона данные больше не закрепляются.

person pstrjds    schedule 09.04.2012

Нет, как только вы покинете блок fixed, значение result больше не будет действительным (оно может случайно оказаться действительным, если GC не запущен).

Правильный способ выполнения такого рода операций — либо иметь ссылку на byte[] в неуправляемой памяти, к которой вы обращаетесь через код C#, либо копировать управляемый массив в неуправляемую память.

person Guvante    schedule 09.04.2012
comment
Вы хотели сказать блок fixeb вместо небезопасного блока? - person JaredPar; 09.04.2012
comment
@JaredPar - Нет, фиксированное и небезопасное - это два разных ключевых слова. Вы, конечно, можете сделать небезопасную переменную фиксированной. - person Security Hound; 09.04.2012
comment
@Ramhound да, но небезопасный блок никак не защищает значения. Объект this может свободно перемещаться по куче GC, даже если он в настоящее время находится в небезопасном блоке. Только fixed блок/ссылка предотвратит перемещение внутри кучи. - person JaredPar; 09.04.2012
comment
Джаред прав. Адрес становится недействительным, когда управление покидает фиксированный блок, а не когда управление покидает небезопасный блок. См. главу 18 спецификации C#. - person Eric Lippert; 09.04.2012
comment
@JaredPar: Да, это был мой мозговой пердеж, значит исправлено, написано небезопасно ›‹. - person Guvante; 10.04.2012