Почему этот небезопасный код генерирует исключение NullReferenceException?
Я играл с небезопасным кодом для проблемы с Code Golf,, и я нашел то, что не могу объяснить. Этот код:
unsafe
{
int i = *(int*)0;
}
Сбой с нарушением доступа (Segfault), но этот код:
unsafe
{
*(int*)0=0;
}
Выбрасывает исключение NullReferenceException. Мне кажется, что первый выполняет чтение, а второй выполняет запись. Исключение говорит мне, что что-то, где-то в CLR перехватывает запись и останавливает ее до того, как ОС уничтожит этот процесс. Почему это происходит при записи, но не на чтение? Это делает segfault для записи, если я делаю значение указателя достаточно большим. Означает ли это, что есть блок памяти, который знает CLR, и не будет даже пытаться писать? Почему же это позволяет мне попытаться прочитать из этого блока? Я что-то совершенно не понимаю?
Edit:
Интересно: System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0);
Дает мне нарушение доступа, а не NullReference.
Ответы
Ответ 1
Первое исключение имеет смысл, конечно же - вы пытаетесь прочитать из адреса памяти 0. Второй интереснее.: P В С++ существует макрос/константа, называемый NULL, который имеет значение 0. Он используется для недопустимых адресов указателей - что очень похоже на нулевое значение в С# для ссылочных типов. Поскольку ссылки С# являются указателями внутри, исключение NullReferenceException возникает, когда вы пытаетесь прочитать/записать с этого адреса - адрес NULL или 0; на самом деле, адреса от 0 до 64K недействительны во всех процессах (в Windows), чтобы уловить ошибки программы. Точное исключение или ошибка может незначительно отличаться на компьютерном оборудовании или версии Windows/.NET Framework, но вы должны получить сообщение об ошибке с обоими фрагментами кода.
Что касается segfault при чтении/записи на случайные адреса, это связано с изоляцией ОС каждого процесса. Вы не можете возиться с кодом или данными других процессов - по крайней мере, не законно.