Исправлен ли указатель? - PullRequest
4 голосов
/ 29 декабря 2011

Есть код, который я читаю, и его часть:

    public static unsafe byte[] GetBytes(uint value, bool BigEndian)
    {
        byte[] buff = new byte[4];
        fixed (byte* pbyte = buff)*((uint*)pbyte) = value;
        if (BigEndian)
            buff.EndianConvert();
        return buff;
    }

Я понимаю, что это просто помещение 4 байтов в месте расположения блока в байтовый массив, но я не совсем понимаю, как.

Мое понимание этого таково:

(byte* pbyte = buff)

создает и возвращает байтовый указатель pbyte, который указывает на адрес баффа,

(uint*)pbyte

Преобразует адрес pbyte в указатель uint?,

Я не понимаю всего остального, хотя. Какая польза от фиксированного ключевого слова? Почему это нельзя сделать так:

(byte* pbyte = buff) = (byte*)value;

Ответы [ 4 ]

2 голосов
/ 29 декабря 2011

Оператор fixed не позволяет сборщику мусора перемещать подвижную переменную. если вы пропустите его, ваш адрес может измениться, и вы получите ошибку

1 голос
/ 29 декабря 2011

Другие объяснили концепцию пиннинга, но я думаю, что вас смутило то, что все это в одной строке.

fixed (byte* pbyte = buff)*((uint*)pbyte) = value;

Эквивалентно:

fixed (byte* pbyte = buff)
{
    *((uint*)pbyte) = value;
}

Так же, как это:

if(someCondition) DoSomething();

Эквивалентно:

if(someCondition)
{
    DoSomething();
}

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

1 голос
/ 29 декабря 2011

Вы не можете просто взять адрес массива, потому что он управляется сборщиком мусора, а это означает, что его местоположение в памяти может измениться в любое время. Ключевое слово fixed закрепляет массив на всем протяжении его области действия, позволяя заполнить четыре байта массива значением (4 байта) uint.

0 голосов
/ 29 декабря 2011

Во время процесса CLR GC для обычных объектов кучи GC (кроме LOH) GC помечает и перемещает все еще живые объекты в следующее поколение (это действие также называется повышенным).При продвижении объекта A GC изменяет адрес объекта A с addr-old на addr-new, а затем обновляет отношения всех объектов, которые ссылаются на эти объекты.

Например, на объект A ссылаютсяобъект B и объект C означают, что объект B имеет указатель, который указывает на объект A, а объект C также имеет указатель, который указывает на объект A. Затем на этапе продвижения адрес объекта A будет изменен сОт addr-old до addr-new, тогда GC также изменит значение указателя ссылки объекта B и объекта C. После модификации у объектов B и C есть правильные указатели, которые все еще указывали на A. Теперь

После выполнения строки «byte * pbyte = buff» у pbyte есть указатель, указывающий на объект «buff», скажем, адрес pbyte равен 0x12345678 (также означает, что addr баффа равен 0x12345678).И вот, GC произошло, объект «buff» будет переведен в новое поколение, что означает, что объект «buff» будет иметь новый адрес памяти, например, «0x55555555».Но «pbyte» является нативным (неуправляемым) объектом, CLR не знает, как поддерживать его жизненный цикл, поэтому, хотя pbyte имеет отношение с buff, но CLR не может изменить адрес pbyte с 0x12345678 на 0x55555555.Затем указатель «pbyte» все еще указывает на адрес 0x12345678, но этот адрес не принадлежит объекту «buff», указатель «pbyte» теперь является плохим указателем.

Оператор «fixed» обеспечитУправляемый объект "бафф" не будет повышен, а также не будет перемещаться отсюда туда.

...